Compare commits

..
Author SHA1 Message Date
Alex Jaramillo d866f468f8 Local Makefile
Signed-off-by: Alex Jaramillo <alex.v.jaramillo@intel.com>
2025-03-21 13:47:54 -07:00
Alex Jaramillo 07d334c718 Fizzing iorecord.c
Signed-off-by: Alex Jaramillo <alex.v.jaramillo@intel.com>
2025-03-21 13:45:44 -07:00
Alex Jaramillo a2367132bd Fuzzing configuration.c
Signed-off-by: Alex Jaramillo <alex.v.jaramillo@intel.com>
2025-03-21 13:13:10 -07:00
Alex Jaramillo ed42b6ed3f Fuzzing libtelemetry
Signed-off-by: Alex Jaramillo <alex.v.jaramillo@intel.com>
2025-03-21 10:02:13 -07:00
Alex Jaramillo d162311e31 telemetry.c: improvements
- tm_set_payload: check payload for NULL value before duplication, this
  is desirable due the use of strnlen and strndup.

- tm_set_payload: replace strlen with strnlen to use MAX_PAYLOAD_LENGTH
  during function call instead of checking after the fact.

Signed-off-by: Alex Jaramillo <alex.v.jaramillo@intel.com>
2025-03-19 15:35:45 -07:00
Alex Jaramillo f23bc1ae12 configure.ac: bump version
Replace clear text record format with json to transmit records to
telemetry collector.

Signed-off-by: Alex Jaramillo <alex.v.jaramillo@intel.com>
2025-03-13 18:32:18 -07:00
Brett T. Warden d0ee68ba43 Publish a JSON message to telemetry server
Replace the custom HTTP headers and plain text payload with a JSON
object. This is the new v3 API implementation.
2025-03-10 12:06:12 -07:00
Brett T. Warden 9bb9288153 Link telemetry post daemon against json-c
also link its test
2025-03-10 12:06:12 -07:00
Brett T. Warden b9ec06fe9a Check for json-c
json-c is now required, in order to construct the JSON payload.
2025-03-10 12:06:12 -07:00
Brett T. Warden 32b306daa9 Update default URL API to v3
Updating the HTTP API from v2 to v3 to support JSON message
encapsulation instead of custom HTTP headers with a single-field POST
field for the payload.
2025-03-10 12:06:12 -07:00
Alex Jaramillo c3b8d81b6d configure.ac: bump version
Increase the version to 2.3.5 to release fix for missing records in
journal and preempt klogscanner service start inside containers.

Signed-off-by: Alex Jaramillo <alex.v.jaramillo@intel.com>
2020-02-24 21:37:06 +00:00
Alex Jaramillo f4f012af7b fix first record missing from journal
This change fixes the condition when the first record after telempostd
starts is not inserted in the telemetry journal.

Notice! there is a change in the logic, while previously records
where inserted to juornal as soon the record was processed. After this
change records will be inserted in the journal only when successfully
delivered or record_server_delivery_enabled is set to false.
2020-02-24 13:27:32 -08:00
Thiago Macieira 9db4e1d8c9 Don't try to start the klogscanner service inside a container
We can't access the klog.

● klogscanner.service - Telemetrics Kernel Log Scanner
     Loaded: loaded (/usr/lib/systemd/system/klogscanner.service; disabled; vendor preset: disabled)
     Active: failed (Result: exit-code) since Fri 2020-01-31 10:44:58 PST; 8min ago
    Process: 21 ExecStart=/usr/bin/klogscanner (code=exited, status=1/FAILURE)
   Main PID: 21 (code=exited, status=1/FAILURE)

Jan 31 10:44:58 <MACHINENAME> systemd[1]: Started Telemetrics Kernel Log Scanner.
Jan 31 10:44:58 <MACHINENAME> klogscanner[21]: ERROR: Cannot read size of kernel ring buffer: Operation not permitted
Jan 31 10:44:58 <MACHINENAME> systemd[1]: klogscanner.service: Main process exited, code=exited, status=1/FAILURE
Jan 31 10:44:58 <MACHINENAME> systemd[1]: klogscanner.service: Failed with result 'exit-code'.
2020-02-05 10:06:08 -08:00
Alex Jaramillo a0efa230b7 configure.ac: bump version
Increase the version to 2.3.4 to release CI configuration changes,
memory leaks fixes, and crash probe frame counter fix.

Signed-off-by: Alex Jaramillo <alex.v.jaramillo@intel.com>
2020-01-30 21:58:59 +00:00
Juro Bystricky f14d6ff4c5 crash_probe.c: process_corefile: reset frame counter
process_corefile may be called twice due to retry logic.
Make sure frame_counter is initialized each time.

Signed-off-by: Juro Bystricky <juro.bystricky@intel.com>
2020-01-30 13:11:26 -08:00
Alex Jaramillo b61b152cb0 replace travis badge for github workflows 2020-01-27 11:05:46 -08:00
Alex Jaramillo ee25d1568d random shift to hprobe timer 2020-01-24 10:47:55 -08:00
avjarami 622fcfca10 pr comments and actions update 2020-01-22 16:08:15 -08:00
avjarami 33609c5607 update CI configuration 2020-01-22 16:08:15 -08:00
avjarami 8ebc882955 fix potential leaks 2020-01-22 16:08:15 -08:00
avjarami d30eb663b3 fixing memory leaks in tests 2020-01-22 16:08:15 -08:00
avjarami 885e325e1e valgrind check for tests 2020-01-22 16:08:15 -08:00
avjarami fffe18a96b Trigger verification on PR opening 2020-01-22 18:03:24 +00:00
Juro Bystricky d9925e1f96 configure.ac: bump version
Increase the version to 2.3.3 due to the fixed buffer
overrun in telemctl.c.

Signed-off-by: Juro Bystricky <juro.bystricky@intel.com>
2020-01-09 13:32:30 -08:00
Juro Bystricky c3ae907651 telemctl.c: fix buffer overflow
The function "concatargs" allocated a buffer for concatenated strings
that was 1 byte short of needed, not accounting for the terminating
NULL char.

Signed-off-by: Juro Bystricky <juro.bystricky@intel.com>
2020-01-09 08:53:42 -08:00
Juro Bystricky 8ca299314c configure.ac: bump version
Increase the version to 2.3.2 to reflect the fact that
bertprobe was entirely removed. The bertprobe functionality
was moved to klogscanner.

Signed-off-by: Juro Bystricky <juro.bystricky@intel.com>
2019-12-17 18:39:20 -08:00
Juro Bystricky 53d4c229a7 klog_scanner.c: truncate payload if needed
Payloads can exceed MAX_PAYLOAD_SIZE, in which case the probe
will fail to send any data to the backend server. This was observed
with BERT payloads, but other payload types can exceed the maximum
size as well.

This patch truncates the payload if needed. It is better to receive
a truncated report than none.

Signed-off-by: Juro Bystricky <juro.bystricky@intel.com>
2019-12-16 10:49:50 -08:00
Juro Bystricky 7a3082eb59 Merge BERT probe with klogscanner
New Linux kernels detect/interpret/display BERT errors in klog.
This makes bertprobe unnecessary, as all the information can
be simply grabbed from klog. This also removes the need to
encode the binary data into HEX/ASCII, so all the encoding
code can be removed as well, including the test suite.

The change was implemented by adding a new pattern for BERT
in the oops_parser.c with some additional minor changes.

Signed-off-by: Juro Bystricky <juro.bystricky@intel.com>
2019-12-13 15:16:20 -08:00
avjarami 071ad2c883 configure.ac: bump version to v2.3.1
Bump version to release fixes to BERT probe.

Signed-off-by: Alex Jaramillo <alex.v.jaramillo@intel.com>
2019-11-25 18:32:38 +00:00
Juro Bystricky 72ea0d9360 bertprobe: fix failures and logging
Fix a reported problem: https://github.com/clearlinux/telemetrics-client/issues/170

This patch fixes the case when there is no error reported via BERT data
and yet the probe fails with error such as:
"Failed to read payload from: /sys/firmware/acpi/tables/data/BERT"
Which then results in journalprobe reporting:
"bert-probe.service: Main process exited, code=exited, status=1/FAILURE"

The file BERT data file now has root only read permissions:

 $ ls -al /sys/firmware/acpi/tables/data/BERT
-r-------- 1 root root 32768 Nov 25 07:37 /sys/firmware/acpi/tables/data/BERT

Hence the user "telemetry" cannot read it anymore. In addition, the file size
can be more than max payload size (4k). However, the file can be completely
blank. The probe inspects the block_status for any present errors.
If there aren't any, probe does not report anything.

While in there, for consistency reasons, modified "printf" statements with
error messages to telem_log(LOG_ERR, msg)

Signed-off-by: Juro Bystricky <juro.bystricky@intel.com>
2019-11-25 10:22:31 -08:00
Juro Bystricky 49823eae83 configure.ac: bump version to v2.3.0
Bump due to switching from opt-out to opt-in.

Signed-off-by: Juro Bystricky <juro.bystricky@intel.com>
2019-11-12 10:26:30 -08:00
Juro Bystricky a12c1d8761 telemctl.c: validate privileges
Not all commands need to be run as root.
In particular "is-active".
While in there, some minor formatting fixes.

Signed-off-by: Juro Bystricky <juro.bystricky@intel.com>
2019-11-12 10:26:30 -08:00
Auke Kok eb1e670b5f Add github actions CI. 2019-11-07 14:04:14 -08:00
Alex Jaramillo 5cbd31cbf2 Require explicit telemctl opt-in
telemetrics-client installation starts when the package is installed,
this change makes sure that to start telemetry the first time two steps
are needed: 1- telemctl opt-in and 2- telemctl start

Signed-off-by: Alex Jaramillo <alex.jch@gmail.com>
2019-10-14 11:02:20 -07:00
Juro Bystricky 4119bdea33 configure.ac: bump version to 2.2.3
Version bump to reflect changes in the package.

Signed-off-by: Juro Bystricky <juro.bystricky@intel.com>
2019-10-08 09:53:02 -07:00
Juro Bystricky 2395f6bf3e python-probe.c: remove python probe
Remove all references to python probe. The python exceptions
are handled entirely in Python code. This telemetrics-client
package does not need to be aware of the Python code exception handling
and does not need to make any provisions for it (such as creating
services and directories).
It is the responsibility of Python exception handling to properly
interface with the telemetry library.

Signed-off-by: Juro Bystricky <juro.bystricky@intel.com>
2019-10-08 09:52:35 -07:00
Tian Baofeng bc7e2610ee pstore_probe.c: add ramoops file support in pstore probe for cl telemetry
Add support for ramoops files in pstore probe, get the crash information
from "/sys/fs/pstore/dmesg-ramoops-x" files.
Add the "-f" cmd option support to input runtime config file when
running the probe.

Change-Id: Id7f26d6e07a7c215027b9be039356b2633d4869f
Signed-off-by: Tian Baofeng <baofeng.tian@intel.com>
2019-09-16 14:16:40 -07:00
Juro Bystricky a4774bde2e configure.ac: bump version to 2.2.2
Version bump from 2.2.1 to 2.2.2

Signed-off-by: Juro Bystricky <juro.bystricky@intel.com>
2019-09-03 09:52:20 -07:00
Juro Bystricky 63574e7406 crash_probe.c: improve error handling
Routine frame_cb: if asprintf fails, assume destination string
is not initialized properly.

Signed-off-by: Juro Bystricky <juro.bystricky@intel.com>
2019-08-30 09:38:33 -07:00
Juro Bystricky ce35876b55 telemdaemon.c: sanity check for record size
handle_client: added a reasonable sanity check for record size.
Currently it is not possible to get max size of headers, so
the assumption is each line in header will be at most 80 chars.

Signed-off-by: Juro Bystricky <juro.bystricky@intel.com>
2019-08-30 09:37:02 -07:00
Juro Bystricky 055e55b2f3 telemdaemon.c: modify handle_client
Split a fairly convoluted loop in "handle_client" into
two distinct steps:

1. Read the record size by reading the first 4 bytes.
2. Once the record size is known, read the rest of the record
   into a buffer.

All the buffer for the record allocation/deallocations are handled
in this routine as well. The new code does not need to know any datails
about the record buffer layout. This needed a minor modification of the
record itself, the first 4 bytes of the record now contain the length of
the entire record (including the 4 bytes). This required a minor change
in "telemtry.c" the routine tm_send_record. The previous "total_size"
in the 4 bytes did not include the terminator and header_size, so
"handle_client" needed to adjust for those to get the real expected
record size.

Also declared "terminate_client" and "process_record" as static.

Signed-off-by: Juro Bystricky <juro.bystricky@intel.com>
2019-08-26 14:27:18 -07:00
Juro Bystricky 84b4266bc2 telempostdaemon.c: fix unitialized variable
Upon (error) exit the field buf.st_blocks may contain garbage.
Make sure buf.st_blocks is deterministic.
While in there, also removed some redundant round brackets.

Signed-off-by: Juro Bystricky <juro.bystricky@intel.com>
2019-08-23 10:07:10 -07:00
Juro Bystricky e7d9d7b0ba telem_recoder_gen.c: remove redundant if statement
Removed the last error test from routine "instanciate_record".

Signed-off-by: Juro Bystricky <juro.bystricky@intel.com>
2019-08-23 09:17:59 -07:00
Juro Bystricky b0effc8850 telemctl.c: changed handling of opt_in/opt_out
Simplified the code for telemctl_opt_out and telemctl_opt_in.
There is no real need to call "access" to determine if opted_out
file exists, we get the same info by scrutinizing the errno.
This fixes any potential TOCTOU problems between access/create and access/unlink.

Signed-off-by: Juro Bystricky <juro.bystricky@intel.com>
2019-08-22 14:57:35 -07:00
Alex Jaramillo e9ce8bc1ae Fix build warning not used variable 2019-08-22 14:57:19 -07:00
Alex Jaramillo ea1480fb04 Allow record creation on opt-out
When telemetry is opt-out, telem-record-gen is unable to print a
telemetry record. This change enables printing a record to stdout
without allowing the record to be submitted.

Signed-off-by: Alex Jaramillo <alex.jch@gmail.com>
2019-08-22 14:57:19 -07:00
Juro Bystricky a6e81aa3bb journal.c: fix some resource leaks
open_journal: Fix leaked file pointer on error exits.

print_journal: Make sure we always call free_journal_entry(entry),
regardless if we print the entry or not.

Signed-off-by: Juro Bystricky <juro.bystricky@intel.com>
2019-08-22 09:12:10 -07:00
Juro Bystricky fffe9755ad journal.c: deserialize_journal_entry fixes
1. Return a known (NULL) value of "*entry" if the function fails.
2. Remove some dead code. Since the loop index ranges 0..4, it is
   pointless to have a switch statement outsized of this range.
3. Ensure all pointers within the allocated structure JournalEntry
   are defined. (They are initialized as NULL courtesy of calloc),
   otherwise we may end up freeing some random pointers in
   "free_journal_entry"

Signed-off-by: Juro Bystricky <juro.bystricky@intel.com>
2019-08-21 16:40:08 -07:00
Juro Bystricky b465d35b24 telemctl.c: avoid using fscanf
Don't use fscanf in order to avoid a potential string overflow.
Use code that is aware of allocated buffer sizes.

Signed-off-by: Juro Bystricky <juro.bystricky@intel.com>
2019-08-21 09:41:07 -07:00
Juro Bystricky e2aeda86eb telem_record_gen.c: fix a potential resource leak
Routine "print_record": always call tm_free_record before
exiting the routine.

Signed-off-by: Juro Bystricky <juro.bystricky@intel.com>
2019-08-21 09:40:55 -07:00
Juro Bystricky 6b4089075f telemctl.c: allow deleting of files not owned by telemetry
Folders owned by "telemetry" may contain files that are not
owned by "telemetry". Allow deleting those.

Signed-off-by: Juro Bystricky <juro.bystricky@intel.com>
2019-08-20 15:08:39 -07:00
Juro Bystricky 83bb3df20a inifile.c: Check for NULL pointer
Don't assume calloc cannot fail, test the return value.

Signed-off-by: Juro Bystricky <juro.bystricky@intel.com>
2019-08-20 15:08:25 -07:00
Juro Bystricky e855a19c88 oops_parser.c: remove needless check
Remove a test for a valid pointer. The test hase been done
already. Also the pointer has been dereferenced already.

Signed-off-by: Juro Bystricky <juro.bystricky@intel.com>
2019-08-20 15:08:10 -07:00
Juro Bystricky 7173e1fede oops_parser.c: detect kernel panic
Add pattern for kernel panic. The new pattern can be tested with:

  $ sudo sh -c "echo 'c' > /proc/sysrq-trigger"

The above command will crash the system immediately, however upon reboot
the pstoreprobe will send the crah report.

Signed-off-by: Juro Bystricky <juro.bystricky@intel.com>
2019-08-20 11:05:07 -07:00
Juro Bystricky a4bc8b55dd iorecord.c: fix a potential race
Prevent time of check to time of use exploit.
Don't use "stat" to get the file size. The "stat" and "fopen"
could refer to two different files.

Signed-off-by: Juro Bystricky <juro.bystricky@intel.com>
2019-08-20 11:04:55 -07:00
Juro Bystricky d81ac1e8f3 telemctl.c: fix resource leak
Close a file descriptor for a freshly created & opened file
when not needed.

Signed-off-by: Juro Bystricky <juro.bystricky@intel.com>
2019-08-20 11:04:38 -07:00
Juro Bystricky b0d448e9e7 telemetry.c: remove unneeded test
tm_set_event_id: if a record exists, then the record headers
array exists as well as it is a part of the record itself.

Signed-off-by: Juro Bystricky <juro.bystricky@intel.com>
2019-08-19 09:29:18 -07:00
Juro Bystricky 88ee43f528 telempostdaemon.c: avoid freeing of uninitialized variables
"save_entry_to_journal": fix potential freeing of uninitilized
variables.

Signed-off-by: Juro Bystricky <juro.bystricky@intel.com>
2019-08-19 09:22:08 -07:00
Juro Bystricky 49a1d41a3a telemdaemon.c: fix for a potential string overflow
In the routine "machine_id_replace" replace "strcpy" with
a safer "strncpy".
Also declare the routine as static as it is not used outside
of this file.

Signed-off-by: Juro Bystricky <juro.bystricky@intel.com>
2019-08-19 09:21:54 -07:00
Juro Bystricky 0e20d62c9e oops_parser.c: remove unneeded check
Remove a useless test.
Routine "stack_frame_append": don't test again if a pointer
is NULL. It has been done already. Also, the pointer was
already dereferenced before the test.

Signed-off-by: Juro Bystricky <juro.bystricky@intel.com>
2019-08-19 09:21:20 -07:00
Juro Bystricky d314c1a88b telemdaemon.c: properly handle mkstemp error
If mkstemp fails, it returns a negative value (-1).
So adjust the test accordingly. Also don't attempt to close
an invalid file descriptor. Don't unlink a file that failed
to be created.

Signed-off-by: Juro Bystricky <juro.bystricky@intel.com>
2019-08-16 14:51:42 -07:00
Juro Bystricky b9ad4c2f14 telem_recorde_gen.c: fix potential memory leaks
It is allowed to enter the same command line argument more than once,
the later value overriding the previous one.
Make sure any previous value is properly de-allocated.

Signed-off-by: Juro Bystricky <juro.bystricky@intel.com>
2019-08-16 13:48:53 -07:00
Juro Bystricky 9fd3c280ba telem_record_gen.c: return correct error in instanciate_record
In the routine "instanciate_record":
If the user passed event_id on the command line and it was incorrect,
any returned error from tm_set_event was ignored and "instanciate_record"
could return success (0).

Signed-off-by: Juro Bystricky <juro.bystricky@intel.com>
2019-08-16 13:48:39 -07:00
Juro Bystricky 75255fff50 telemdaemon.c: don't assume correct machine_id length
"fscanf" assumes an arbitrarily long string, so limit the scan to
32 characters to avoid a potential buffer overflow.

Signed-off-by: Juro Bystricky <juro.bystricky@intel.com>
2019-08-16 13:48:25 -07:00
Juro Bystricky b2adc08d4f util.c: correct return value from "get_header_value"
Even if errors were encounterd parsing the header we used to
return success ("true") by a mistake.
Fix this by correcting the test.

Signed-off-by: Juro Bystricky <juro.bystricky@intel.com>
2019-08-16 13:47:58 -07:00
Juro Bystricky e6357bb051 iorecord.c: fix memory allocation check
Signed-off-by: Juro Bystricky <juro.bystricky@intel.com>
2019-08-16 10:42:11 -07:00
Mohammed Khajapasha 15b5df2f47 Fix double free in deserialize_journal_entry and print_journal
Causing double free case when an empty line has been
passed to deserialize_journal_entry() from print_journal()
to print the report, This case double free can be occured
at print_journal() and deserialize_journal_entry().

Signed-off-by: Mohammed Khajapasha <mohammed.khajapasha@intel.com>
2019-08-12 10:00:42 -07:00
Juro Bystricky f516d85e59 telemctl: binary version
Create a binary executable "telemctl" that is equivalent
to the shell script "telemctl".
Main reason is to make (future) localization easier.

Signed-off-by: Juro Bystricky <juro.bystricky@intel.com>
2019-08-06 09:57:29 -07:00
Ammy Yi ce2ad0c2b0 Add "ACPI Error:" pattern for ACPI issues.
If "ACPI Error:" happen, it means there is kernel or bios issue.

Signed-off-by: Ammy Yi <ammy.yi@intel.com>
2019-08-06 09:51:54 -07:00
Juro Bystricky afb7572adf configure.ac: bump version to 2.2.1
Version bump from 2.2.0 to 2.2.1

Signed-off-by: Juro Bystricky <juro.bystricky@intel.com>
2019-08-02 15:05:03 -07:00
Juro Bystricky 3337c45b08 man pages: remove references to telemd
"telemd" was replaced by "telemprobd" a long time
ago, however several man pages still contain refererences
to "telemd".
This patch replaces all references accordingly.

Signed-off-by: Juro Bystricky <juro.bystricky@intel.com>
2019-08-02 15:04:43 -07:00
Juro Bystricky 672e741e5d Fix build for logtype=systemd
Build is broken for multiple binaries when configured for logging
to systemd journal:

$ ./configure --enable-logtype=systemd
$ make

All binaries that use the routine "telem_log" must link to additional
libraries when logging to systemd journal.

Signed-off-by: Juro Bystricky <juro.bystricky@intel.com>
2019-07-29 09:57:46 -07:00
Juro Bystricky e19e72a5b3 crash_probe: Limit the size of backtrace
A simple program that is highly recursive (or very deeply nested)
and causes stack overflow or segmentation fault can take hours
for crash_probe to process.
This patch fixes this by limiting the size of the stacktrace to 64.
(64 is also the size of the stack trace used by systemd).
64 lines of 80 characters == 5120 bytes. Enough to leave room for
telemetry headers and have a reasonable chance of making the 8k payload
limit.

If the stack trace is truncated, the payload will contain a message:

"Too many frames. Backtrace truncated".

Signed-off-by: Juro Bystricky <juro.bystricky@intel.com>
2019-07-25 08:40:04 -07:00
Juro Bystricky 9a25115d8e configure.ac: bump version to 2.2.0
Version bump from 2.1.1 to 2.2.0

Signed-off-by: Juro Bystricky <juro.bystricky@intel.com>
2019-07-09 09:36:31 -07:00
Juro Bystricky 11c5808fd9 telem_journal: improve usage message
No functional changes.
Provide additional info in the usage message, based on user feedback.
While in there, some additional minor changes..

Signed-off-by: Juro Bystricky <juro.bystricky@intel.com>
2019-06-12 10:42:33 -07:00
Juro Bystricky 9f67a1ca8a Updated AUTHORS
Added recent authors.
Also removed TODO, as it has not been maintained for several years.

Signed-off-by: Juro Bystricky <juro.bystricky@intel.com>
2019-06-12 10:40:30 -07:00
Juro Bystricky 781d9382d0 Fix compiler warnings [-Wstringop-truncation]
Modify code to avoid generating compiler warning such as:
warning: ‘__builtin_strncpy’ output truncated before terminating nul copying as many bytes from a string as its length [-Wstringop-truncation]

Signed-off-by: Juro Bystricky <juro.bystricky@intel.com>
2019-06-05 10:56:52 -07:00
Juro Bystricky 2a29191c5f telem_record_gen.c: fix for compiler warning [-Wstringop-overflow=]
Replace strncpy with memcpy to avoid GCC9 warning:
warning: ‘__builtin___strncpy_chk’ specified bound depends on the length of the source argument [-Wstringop-overflow=]

There is no danger of overflow. The destination buffer is guaranteed to
be of MAX_PAYLOAD_LENGTH.

Signed-off-by: Juro Bystricky <juro.bystricky@intel.com>
2019-06-05 10:56:36 -07:00
Juro Bystricky c264cd044c check_probes.c: fix for compiler warning [-Wstringop-overflow=]
Replaced strncpy with memcpy and added some buffer overflow checks in
order to avoid GCC9 compiler warning:
warning: ‘__builtin___strncpy_chk’ specified bound depends on the length of the source argument [-Wstringop-overflow=]

While in there, removed one unused global variable and declared the remaining
global variables as static.

Signed-off-by: Juro Bystricky <juro.bystricky@intel.com>
2019-06-05 10:56:18 -07:00
California Sullivan ad4dde288b journal.c: reduce severity of message when failing to opening old records
The default configuration for telemetry is to not store records that
were successfully sent. This would result in a multitude of errors when
calling the journal program with --include_record, as the records no
longer existed to open.

Signed-off-by: California Sullivan <california.l.sullivan@intel.com>
2019-06-03 14:23:34 -07:00
Alex Jaramillo 48527a343b Protect security checks from removal
Compiler optimization can remove certain security checks for undefined
behavior. Add compiler flags that restrict arbitrary decisions when
handling undefined behaviors.

* fno-strict-overflow, do not assume signed overflow does not occur.
* fno-delete-null-pointer-checks, do not assume null pointer deference
does not exists.
* fwrapv, always wrap signed overflow.

Signed-off-by: Alex Jaramillo <alex.jch@gmail.com>
2019-05-30 11:02:44 -07:00
California Sullivan d13073ff25 src: standardize log and debug messages
Use the telem_log, telem_debug, and telem_perror macros to print
messages instead of printf and fprintf where appropriate. These macros
may be configured to print to many standard locations at compile time,
such as the syslog, the journal, or stderr. The remaining uses of
fprintf in printing errors is only for command line utilities.

When using telem_debug, we don't need to surround it with #ifdef DEBUG
statements, as the macro does that itself.

Finally, stop hiding real errors behind #ifdef DEBUGs. If an error
occurs, it should be logged.

Signed-off-by: California Sullivan <california.l.sullivan@intel.com>
2019-05-02 14:44:35 -07:00
California Sullivan 43c506382a log.h: fix telem_debug macro
The telem_debug macro was apparently never tested previously, as it
didn't compile.

Signed-off-by: California Sullivan <california.l.sullivan@intel.com>
2019-05-02 14:44:35 -07:00
Juro Bystricky aca23d1c54 Minor houskeeping
Moved a misplaced comment to where it belongs.
Fixed incorrectly formatted log string.

Signed-off-by: Juro Bystricky <juro.bystricky@intel.com>
2019-04-25 09:27:24 -07:00
Juro Bystricky 2639e4d042 daemons: name space separation
The telempostd and telemprobd daemons both used identical
name "initialize_daemon" for daemon initialization.
Although the name was identical, the code for each daemon initialization
was different, leading to some potential confusion.
Also moved telemprobd specific code "stage_record" from iorecord.c to
telemdaemon.c
Modified local.mk accordingly.

Signed-off-by: Juro Bystricky <juro.bystricky@intel.com>
2019-04-22 19:45:40 -07:00
California Sullivan cc70c3ffba telemetry.c: support variants in the system_name header
Read the VARIANT_ID field of the os-release file and add it to our
system_name header. This allows differentiation of different variants of
an operating system on the backend.

The header will be unchanged on operating systems without a VARIANT_ID
in their os-release file. Also continue limiting the header length to 80
characters.

Signed-off-by: California Sullivan <california.l.sullivan@intel.com>
2019-04-22 10:31:09 -07:00
California Sullivan ba62496248 telempostdaemon: fix retries loop
The 'else if (retry_attempt == MAX_RETRY_ATTEMPTS)' section would get
hit the first time through the loop on every invocation, printing the
failed to deliver error to the journal.

To fix it, initialize retry_attempt to zero and check that we're above
zero retries to enter retry sections.

This fixes the false "Record deliver failed after 8 attempts" message
that was getting spewed into the journal.

Signed-off-by: California Sullivan <california.l.sullivan@intel.com>
2019-04-22 10:30:45 -07:00
California Sullivan d873c4e067 configuration: rework to allow layered configuration
Have a default configuration set in the binary, with conf files only
being used to change from the defaults. This allows making simpler
configs which only toggle specific options, which may be useful for user
configuration or testing. See src/data/example.2.conf for an example of
a new simplified config.

By default the telemetrics-client is configured with
https://clr.telemetry.intel.com/v2/collector as the built-in server
location. This is the normal Clear Linux telemetrics backend. It can
be changed via the configure flag --with-backendserveraddr=URI. This
allows anyone else using this project to easily specify their own
default backend without patching. As per usual, users can specify a
different server location in a configuration file.

Signed-off-by: California Sullivan <california.l.sullivan@intel.com>
2019-04-22 10:29:59 -07:00
Juro Bystricky 335ed440bc telem_record_gen: various changes
1. Declare all local routines as static
2. Added support for custom config files (implemented the option "-f")
3. bugfix: -P option with non existing file would not fail.
4. More stringent parsing of classification, avoid hard coded numbers,
   use pre-defined constants from "common.h" instead.
   Also detect MAX_SUBCAT_LENGTH violation.
5. Some local routines were declared as "bool" instead of "int":
   These routines used to return 1 if successful and 0 if failed.
   This was confusing as all API routines return 0 upon success
   and non-zero value if error.
6. Replaced the whole routine "allocate_payload_buffer" with a single
   call to "calloc".

Signed-off-by: Juro Bystricky <juro.bystricky@intel.com>
2019-04-22 09:30:02 -07:00
Juro Bystricky 297e59fe5b Improve support of non-default configuration files.
Most probes allow passing of non-default configuration file
via command line using the "-f" switch.
For example:

$ hprobe -f custom_cfg_file.conf

If the file "custom_cfg_file.conf" contains

server = http://<my backend server>

one would expect the hprobe payload will be sent to the server
http://<my backend server>. However, this is not the case, as the
various daemons delivering the payload to the backend are blissfully
unaware of the of the config file hprobe wanted to use.

The solution is to include the absolute path of the config file
specified on the command line as part of the payload. Once the
payload is about to be sent via the routine "post_record_http",
the routine checks if a non-default config file was requested.
If so, configuration is re-initialized with the file.
Upon exit, the routine re-initializes the original (default) configuration.

The non-default file may not exist at the send time anymore,
for example when sending some spooled records. In that case we
intentionally don't send anything to the backend.

If the record does not contain the optional configuration file
information, it's business as usual.

Signed-off-by: Juro Bystricky <juro.bystricky@intel.com>
2019-04-22 09:29:30 -07:00
Juro Bystricky 4a2cfd99ad spool.c: fix memory leak
In case of memory allocation failures or record parsing failures,
"transmit_spooled_record" may have returned without freeing any previously
allocated memory.

Signed-off-by: Juro Bystricky <juro.bystricky@intel.com>
2019-03-22 14:37:33 -07:00
Alex Jaramillo 46f2e0c8cb Release v2.1.1 2019-02-25 21:14:14 +00:00
Juro Bystricky ffc0554346 pythonprobe: fix folders
Python3 expects two "well known" folders to exist in order to generate exception
payloads:

/var/tmp/telemetry
/var/lib/telemetry/python

Both folders must exist at the above absolute locations in order to properly
create and submit Python3 telemetry unhandled exception payload.

Signed-off-by: Juro Bystricky <juro.bystricky@intel.com>
2019-02-25 13:07:06 -08:00
92 changed files with 2546 additions and 1901 deletions
+31
View File
@@ -0,0 +1,31 @@
name: C/C++ CI
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: install dependencies
run: sudo apt-get install libcurl4-gnutls-dev valgrind libelf-dev libdw-dev
- name: install check
run: wget https://github.com/libcheck/check/releases/download/0.12.0/check-0.12.0.tar.gz &&
tar xf check-0.12.0.tar.gz &&
pushd check-0.12.0 &&
./configure --prefix=/usr --libdir=/usr/lib/x86_64-linux-gnu &&
make -j8 && sudo make install && popd
- name: autogen
run: sh autogen.sh
- name: configure
run: ./configure
- name: make
run: make -j8
- name: check
run: make check
- name: valgrind memcheck
run: make check-valgrind
- name: make distcheck
run: make distcheck
+1 -3
View File
@@ -35,14 +35,13 @@ build-aux/m4/ltversion.m4
/tags
telemprobd
telempostd
telemctl
autoscan.log
configure.scan
tprobe
hprobe
bertprobe
crashprobe
journalprobe
pythonprobe
telem-record-gen
klogscanner
pstoreclean
@@ -58,7 +57,6 @@ tests/check_config
tests/check_daemon
tests/check_journal
tests/check_libtelemetry
tests/check_ncb64
tests/check_postd
tests/check_probd
tests/check_probes
-35
View File
@@ -1,35 +0,0 @@
dist: trusty
compiler:
- gcc
os:
- linux
before_script:
./autogen.sh
language: c
addons:
apt:
sources:
- ubuntu-toolchain-r-test
packages:
- libcurl4-gnutls-dev
- valgrind
- autoconf
- automake
- check
- libelf-dev
- libdw-dev
install:
- 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
after_failure: cat test-suite.log
+3
View File
@@ -3,3 +3,6 @@ Patrick McCarty <patrick.mccarty@intel.com>
Rob Nesius <robert.a.nesius@intel.com>
Gabrielle N. Beyer <gabrielle.n.beyer@intel.com>
Pam Leonard <pam.leonard@intel.com>
Alex V. Jaramillo <alex.v.jaramillo@intel.com>
California Sullivan <california.l.sullivan@intel.com>
Juro Bystricky <juro.bystricky@intel.com>
+1 -1
View File
@@ -15,13 +15,13 @@ DISTCHECK_CONFIGURE_FLAGS = \
include $(top_srcdir)/build-aux/make/cflags.make
include $(top_srcdir)/build-aux/make/ldflags.make
include $(top_srcdir)/docs/local.mk
include $(top_srcdir)/scripts/local.mk
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
include $(top_srcdir)/fuzz/local.mk
release:
@git rev-parse v$(PACKAGE_VERSION) &> /dev/null; \
+25 -16
View File
@@ -1,4 +1,4 @@
[![Build Status](https://travis-ci.org/clearlinux/telemetrics-client.svg?branch=master)](https://travis-ci.org/clearlinux/telemetrics-client)
![](https://github.com/clearlinux/telemetrics-client/workflows/C%2FC++%20CI/badge.svg)
Telemetrics-client
==================
@@ -67,38 +67,47 @@ Descriptions of config options are listed below in the Usage section.
Starting the client
---------------------
To use the telemetrics client a one time explicit ```opt-in``` is required (this is
also true when the contents of the directory ```/etc/telemetrics/``` are removed).
To opt-in to telemetrics-client use the command:
```{r, engine='bash', count_lines}
telemctl opt-in
```
**Note** this is a change from previous versions, before 2.3.0 installation of
telemetrics client was enough to enable the client and if needed the client could
be disabled with ```telemctl opt-out```. This command in previous versions created
```/etc/telemetrics/opt-out``` file (after telemetrics-client version 2.3.0 this
file can be safely removed).
If the client was compiled with systemd support the respective activation units
should be already in place (after a ```make install``` invocation). In this case
the client wil start automatically when data is made available to it. i.e. when
executing an ```/usr/bin/hprobe``` command.
Method 1 (recommended):
executing an ```/usr/bin/hprobe``` command. Otherwise use the following command:
```{r, engine='bash', count_lines}
telemctl start
```
Note: the above invocation technically readies the service for both socket and
path activation, so you may not see an "active" status.
Method 2:
path activation, so you may not see an "active" status. To check the status of
telemetrics-client use:
```{r, engine='bash', count_lines}
systemctl start telemprobd.service
systemctl start telempostd.service
telemctl is-active
telemprobd : active
telempostd : active
```
Method 3:
```{r, engine='bash', count_lines}
telemprobd &
telempostd &
```
Starting individual service units ```telempostd.service``` or ```telemeprobd.service```
is discouraged.
Configure the client to autostart at boot
---------------------
As longs as the first time ```opt-in``` was performed, the following methods are valid:
Method 1 (recommended):
Enable the socket-activated service and path unit:
-24
View File
@@ -1,24 +0,0 @@
Build
=====
- make sure our AM_CFLAGS do not break older compilers, e.g. compilers
that do not support -fstack-protector-strong
Probes
======
- add pstore probe (Archana)
- journal probe: detect kernel oopses (Patrick)
- crash probe: add function/register value info to crash payloads (Patrick)
- crash probe: if some symbols are missing from backtraces, attempt the
debuginfo lookup again after a certain timeout (Patrick)
- crash probe: for non-debug builds, ensure that we are invoked by the kernel,
and write tests to verify that attempts to run the crash probe for non-debug
builds fail. (Patrick)
Library
=======
- add API for structured payloads
Other
=====
- set fixed size for system-wide core file ulimit, and afterward notify QA team
to update the BAT test that checks for the ulimit value.
+239
View File
@@ -0,0 +1,239 @@
# ===========================================================================
# https://www.gnu.org/software/autoconf-archive/ax_valgrind_check.html
# ===========================================================================
#
# SYNOPSIS
#
# AX_VALGRIND_DFLT(memcheck|helgrind|drd|sgcheck, on|off)
# AX_VALGRIND_CHECK()
#
# DESCRIPTION
#
# AX_VALGRIND_CHECK checks whether Valgrind is present and, if so, allows
# running `make check` under a variety of Valgrind tools to check for
# memory and threading errors.
#
# Defines VALGRIND_CHECK_RULES which should be substituted in your
# Makefile; and $enable_valgrind which can be used in subsequent configure
# output. VALGRIND_ENABLED is defined and substituted, and corresponds to
# the value of the --enable-valgrind option, which defaults to being
# enabled if Valgrind is installed and disabled otherwise. Individual
# Valgrind tools can be disabled via --disable-valgrind-<tool>, the
# default is configurable via the AX_VALGRIND_DFLT command or is to use
# all commands not disabled via AX_VALGRIND_DFLT. All AX_VALGRIND_DFLT
# calls must be made before the call to AX_VALGRIND_CHECK.
#
# If unit tests are written using a shell script and automake's
# LOG_COMPILER system, the $(VALGRIND) variable can be used within the
# shell scripts to enable Valgrind, as described here:
#
# https://www.gnu.org/software/gnulib/manual/html_node/Running-self_002dtests-under-valgrind.html
#
# Usage example:
#
# configure.ac:
#
# AX_VALGRIND_DFLT([sgcheck], [off])
# AX_VALGRIND_CHECK
#
# in each Makefile.am with tests:
#
# @VALGRIND_CHECK_RULES@
# VALGRIND_SUPPRESSIONS_FILES = my-project.supp
# EXTRA_DIST = my-project.supp
#
# This results in a "check-valgrind" rule being added. Running `make
# check-valgrind` in that directory will recursively run the module's test
# suite (`make check`) once for each of the available Valgrind tools (out
# of memcheck, helgrind and drd) while the sgcheck will be skipped unless
# enabled again on the commandline with --enable-valgrind-sgcheck. The
# results for each check will be output to test-suite-$toolname.log. The
# target will succeed if there are zero errors and fail otherwise.
#
# Alternatively, a "check-valgrind-$TOOL" rule will be added, for $TOOL in
# memcheck, helgrind, drd and sgcheck. These are useful because often only
# some of those tools can be ran cleanly on a codebase.
#
# The macro supports running with and without libtool.
#
# LICENSE
#
# Copyright (c) 2014, 2015, 2016 Philip Withnall <philip.withnall@collabora.co.uk>
#
# Copying and distribution of this file, with or without modification, are
# permitted in any medium without royalty provided the copyright notice
# and this notice are preserved. This file is offered as-is, without any
# warranty.
#serial 17
dnl Configured tools
m4_define([valgrind_tool_list], [[memcheck], [helgrind], [drd], [sgcheck]])
m4_set_add_all([valgrind_exp_tool_set], [sgcheck])
m4_foreach([vgtool], [valgrind_tool_list],
[m4_define([en_dflt_valgrind_]vgtool, [on])])
AC_DEFUN([AX_VALGRIND_DFLT],[
m4_define([en_dflt_valgrind_$1], [$2])
])dnl
AM_EXTRA_RECURSIVE_TARGETS([check-valgrind])
m4_foreach([vgtool], [valgrind_tool_list],
[AM_EXTRA_RECURSIVE_TARGETS([check-valgrind-]vgtool)])
AC_DEFUN([AX_VALGRIND_CHECK],[
dnl Check for --enable-valgrind
AC_ARG_ENABLE([valgrind],
[AS_HELP_STRING([--enable-valgrind], [Whether to enable Valgrind on the unit tests])],
[enable_valgrind=$enableval],[enable_valgrind=])
AS_IF([test "$enable_valgrind" != "no"],[
# Check for Valgrind.
AC_CHECK_PROG([VALGRIND],[valgrind],[valgrind])
AS_IF([test "$VALGRIND" = ""],[
AS_IF([test "$enable_valgrind" = "yes"],[
AC_MSG_ERROR([Could not find valgrind; either install it or reconfigure with --disable-valgrind])
],[
enable_valgrind=no
])
],[
enable_valgrind=yes
])
])
AM_CONDITIONAL([VALGRIND_ENABLED],[test "$enable_valgrind" = "yes"])
AC_SUBST([VALGRIND_ENABLED],[$enable_valgrind])
# Check for Valgrind tools we care about.
[valgrind_enabled_tools=]
m4_foreach([vgtool],[valgrind_tool_list],[
AC_ARG_ENABLE([valgrind-]vgtool,
m4_if(m4_defn([en_dflt_valgrind_]vgtool),[off],dnl
[AS_HELP_STRING([--enable-valgrind-]vgtool, [Whether to use ]vgtool[ during the Valgrind tests])],dnl
[AS_HELP_STRING([--disable-valgrind-]vgtool, [Whether to skip ]vgtool[ during the Valgrind tests])]),
[enable_valgrind_]vgtool[=$enableval],
[enable_valgrind_]vgtool[=])
AS_IF([test "$enable_valgrind" = "no"],[
enable_valgrind_]vgtool[=no],
[test "$enable_valgrind_]vgtool[" ]dnl
m4_if(m4_defn([en_dflt_valgrind_]vgtool), [off], [= "yes"], [!= "no"]),[
AC_CACHE_CHECK([for Valgrind tool ]vgtool,
[ax_cv_valgrind_tool_]vgtool,[
ax_cv_valgrind_tool_]vgtool[=no
m4_set_contains([valgrind_exp_tool_set],vgtool,
[m4_define([vgtoolx],[exp-]vgtool)],
[m4_define([vgtoolx],vgtool)])
AS_IF([`$VALGRIND --tool=]vgtoolx[ --help >/dev/null 2>&1`],[
ax_cv_valgrind_tool_]vgtool[=yes
])
])
AS_IF([test "$ax_cv_valgrind_tool_]vgtool[" = "no"],[
AS_IF([test "$enable_valgrind_]vgtool[" = "yes"],[
AC_MSG_ERROR([Valgrind does not support ]vgtool[; reconfigure with --disable-valgrind-]vgtool)
],[
enable_valgrind_]vgtool[=no
])
],[
enable_valgrind_]vgtool[=yes
])
])
AS_IF([test "$enable_valgrind_]vgtool[" = "yes"],[
valgrind_enabled_tools="$valgrind_enabled_tools ]m4_bpatsubst(vgtool,[^exp-])["
])
AC_SUBST([ENABLE_VALGRIND_]vgtool,[$enable_valgrind_]vgtool)
])
AC_SUBST([valgrind_tools],["]m4_join([ ], valgrind_tool_list)["])
AC_SUBST([valgrind_enabled_tools],[$valgrind_enabled_tools])
[VALGRIND_CHECK_RULES='
# Valgrind check
#
# Optional:
# - VALGRIND_SUPPRESSIONS_FILES: Space-separated list of Valgrind suppressions
# files to load. (Default: empty)
# - VALGRIND_FLAGS: General flags to pass to all Valgrind tools.
# (Default: --num-callers=30)
# - VALGRIND_$toolname_FLAGS: Flags to pass to Valgrind $toolname (one of:
# memcheck, helgrind, drd, sgcheck). (Default: various)
# Optional variables
VALGRIND_SUPPRESSIONS ?= $(addprefix --suppressions=,$(VALGRIND_SUPPRESSIONS_FILES))
VALGRIND_FLAGS ?= --num-callers=30
VALGRIND_memcheck_FLAGS ?= --leak-check=full --show-reachable=no
VALGRIND_helgrind_FLAGS ?= --history-level=approx
VALGRIND_drd_FLAGS ?=
VALGRIND_sgcheck_FLAGS ?=
# Internal use
valgrind_log_files = $(addprefix test-suite-,$(addsuffix .log,$(valgrind_tools)))
valgrind_memcheck_flags = --tool=memcheck $(VALGRIND_memcheck_FLAGS)
valgrind_helgrind_flags = --tool=helgrind $(VALGRIND_helgrind_FLAGS)
valgrind_drd_flags = --tool=drd $(VALGRIND_drd_FLAGS)
valgrind_sgcheck_flags = --tool=exp-sgcheck $(VALGRIND_sgcheck_FLAGS)
valgrind_quiet = $(valgrind_quiet_$(V))
valgrind_quiet_ = $(valgrind_quiet_$(AM_DEFAULT_VERBOSITY))
valgrind_quiet_0 = --quiet
valgrind_v_use = $(valgrind_v_use_$(V))
valgrind_v_use_ = $(valgrind_v_use_$(AM_DEFAULT_VERBOSITY))
valgrind_v_use_0 = @echo " USE " $(patsubst check-valgrind-%-am,%,$''@):;
# Support running with and without libtool.
ifneq ($(LIBTOOL),)
valgrind_lt = $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=execute
else
valgrind_lt =
endif
# Use recursive makes in order to ignore errors during check
check-valgrind-am:
ifeq ($(VALGRIND_ENABLED),yes)
$(A''M_V_at)$(MAKE) $(AM_MAKEFLAGS) -k \
$(foreach tool, $(valgrind_enabled_tools), check-valgrind-$(tool))
else
@echo "Need to reconfigure with --enable-valgrind"
endif
# Valgrind running
VALGRIND_TESTS_ENVIRONMENT = \
$(TESTS_ENVIRONMENT) \
env VALGRIND=$(VALGRIND) \
G_SLICE=always-malloc,debug-blocks \
G_DEBUG=fatal-warnings,fatal-criticals,gc-friendly
VALGRIND_LOG_COMPILER = \
$(valgrind_lt) \
$(VALGRIND) $(VALGRIND_SUPPRESSIONS) --error-exitcode=1 $(VALGRIND_FLAGS)
define valgrind_tool_rule
check-valgrind-$(1)-am:
ifeq ($$(VALGRIND_ENABLED)-$$(ENABLE_VALGRIND_$(1)),yes-yes)
ifneq ($$(TESTS),)
$$(valgrind_v_use)$$(MAKE) check-TESTS \
TESTS_ENVIRONMENT="$$(VALGRIND_TESTS_ENVIRONMENT)" \
LOG_COMPILER="$$(VALGRIND_LOG_COMPILER)" \
LOG_FLAGS="$$(valgrind_$(1)_flags)" \
TEST_SUITE_LOG=test-suite-$(1).log
endif
else ifeq ($$(VALGRIND_ENABLED),yes)
@echo "Need to reconfigure with --enable-valgrind-$(1)"
else
@echo "Need to reconfigure with --enable-valgrind"
endif
endef
$(foreach tool,$(valgrind_tools),$(eval $(call valgrind_tool_rule,$(tool))))
A''M_DISTCHECK_CONFIGURE_FLAGS ?=
A''M_DISTCHECK_CONFIGURE_FLAGS += --disable-valgrind
MOSTLYCLEANFILES ?=
MOSTLYCLEANFILES += $(valgrind_log_files)
.PHONY: check-valgrind $(add-prefix check-valgrind-,$(valgrind_tools))
']
AC_SUBST([VALGRIND_CHECK_RULES])
m4_ifdef([_AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE([VALGRIND_CHECK_RULES])])
])
+6 -2
View File
@@ -12,7 +12,10 @@ AM_CFLAGS = \
-Wunreachable-code \
-funsigned-char \
-fPIE \
-fPIC
-fPIC \
-fno-strict-overflow \
-fno-delete-null-pointer-checks \
-fwrapv
AM_CPPFLAGS = \
-D_FORTIFY_SOURCE=2 \
@@ -25,6 +28,7 @@ AM_CPPFLAGS = \
-DDATADIR=\"$(datadir)\" \
-DLOCALSTATEDIR=\"$(localstatedir)\" \
-DPSTOREDIR=\"$(localstatedir)/cache/telemetry/pstore\" \
-DTESTOOPSDIR=\"$(top_srcdir)/tests/oops_test_files\"
-DTESTOOPSDIR=\"$(top_srcdir)/tests/oops_test_files\" \
-DBACKEND_ADDR=\"$(BACKEND_ADDR)\"
# vim: filetype=automake tabstop=8 shiftwidth=8 noexpandtab
+18 -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], [2.1.0], [https://clearlinux.org/])
AC_INIT([telemetrics-client], [2.4.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])
@@ -20,11 +20,20 @@ AC_PROG_INSTALL
AC_PROG_LN_S
AC_PROG_MAKE_SET
# Valgrind check
AX_VALGRIND_DFLT(memcheck, on)
AX_VALGRIND_DFLT(helgrind, off)
AX_VALGRIND_DFLT(drd, off)
AX_VALGRIND_DFLT(sgcheck, off)
AX_VALGRIND_CHECK
# Checks for libraries.
# check >= 0.9.12 is required for TAP output
PKG_CHECK_MODULES([CHECK], [check >= 0.12])
PKG_CHECK_MODULES([CURL], [libcurl])
PKG_CHECK_MODULES([JSON_C], [json-c])
AC_CHECK_LIB([elf], [elf_begin], [have_elflib=yes], [AC_MSG_ERROR([Unable to find libelf from elfutils])])
AC_CHECK_LIB([dw], [dwfl_begin], [have_dwlib=yes], [AC_MSG_ERROR([Unable to find libdw from elfutils])])
AS_IF([test "x$have_elflib" = "xyes" -a "x$have_dwlib" = "xyes"],
@@ -41,6 +50,7 @@ AC_CHECK_HEADERS([elfutils/libdwfl.h])
AC_CHECK_HEADERS([fcntl.h])
AC_CHECK_HEADERS([getopt.h])
AC_CHECK_HEADERS([inttypes.h])
AC_CHECK_HEADERS([json-c/json.h])
AC_CHECK_HEADERS([libelf.h])
AC_CHECK_HEADERS([limits.h])
AC_CHECK_HEADERS([stdarg.h])
@@ -119,6 +129,11 @@ AC_ARG_WITH([loglevel], AS_HELP_STRING([--with-loglevel=NUM],
[loglevel=5])
AC_DEFINE_UNQUOTED([MAX_LOG_LEVEL], [${loglevel}], [Maximum log level for binaries])
AC_ARG_WITH([backendserveraddr], AS_HELP_STRING([--with-backendserveraddr=URI],
[uri to telemetrics backend server @<:@default=https://clr.telemetry.intel.com/v3/collector@:>@]), [backendaddr=${withval}])
test -z "${backendaddr}" && backendaddr=https://clr.telemetry.intel.com/v3/collector
AC_SUBST(BACKEND_ADDR, [${backendaddr}])
AC_ARG_ENABLE([logtype], AS_HELP_STRING([--enable-logtype],
[Vector for logging: stderr (default), syslog, systemd]),
[case ${enableval} in
@@ -166,4 +181,6 @@ systemconfdir: $confpath
socketdir: $socketpath
loglevel: $loglevel
logtype: $logtype
valgrind: $enable_valgrind
])
+1 -1
View File
@@ -89,7 +89,7 @@ Event id to use in the record. If not provided a randomly generated id will be a
.SH SEE ALSO
.INDENT 0.0
.IP \(bu 2
\fBtelemd\fP(1)
\fBtelemprobd\fP(1)
.IP \(bu 2
\fI\%https://github.com/clearlinux/telemetrics\-client\fP
.IP \(bu 2
+1 -1
View File
@@ -76,6 +76,6 @@ RETURN VALUES
SEE ALSO
========
* ``telemd``\(1)
* ``telemprobd``\(1)
* https://github.com/clearlinux/telemetrics-client
* https://clearlinux.org/documentation/
+7 -6
View File
@@ -34,7 +34,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.sp
\fBtelemctl\fP
.sp
\fB/etc/telemetrics/opt\-out\fP
\fB/etc/telemetrics/opt\-in\fP
.SH DESCRIPTION
.sp
Control actions for telemetry services. The command can be used to start,
@@ -48,12 +48,13 @@ restart, or stop \fBtelemprobd\fP(1) and \fBtelempostd\fP(1), or to opt\-in or o
Starts, stops or restarts all running telemetry services.
.IP \(bu 2
\fBopt\-in\fP:
Opts in to telemetry, and starts telemetry services. The opt\-out file
\fB/etc/telemetrics/opt\-out\fP is removed.
Opts in to telemetry and the opt\-in file \fB/etc/telemetrics/opt\-in\fP
is created. Note: this is a one time required operation before
telemetry can be used the first time.
.IP \(bu 2
\fBopt\-out\fP:
Opts out of telemetry, and stops telemetry services. The opt\-out file
\fB/etc/telemetrics/opt\-out\fP is created.
Opts out of telemetry, and stops telemetry services. The opt\-in file
\fB/etc/telemetrics/opt\-in\fP is deleted.
.IP \(bu 2
\fBis\-active\fP:
Checks if telemetry client daemons are active (telemprobd and telempostd).
@@ -66,7 +67,7 @@ Checks if telemetry client daemons are active (telemprobd and telempostd).
.SH SEE ALSO
.INDENT 0.0
.IP \(bu 2
\fBtelemd\fP(1)
\fBtelemprobd\fP(1)
.IP \(bu 2
\fI\%https://github.com/clearlinux/telemetrics\-client\fP
.IP \(bu 2
+7 -6
View File
@@ -15,7 +15,7 @@ SYNOPSIS
``telemctl``
``/etc/telemetrics/opt-out``
``/etc/telemetrics/opt-in``
DESCRIPTION
@@ -32,12 +32,13 @@ OPTIONS
Starts, stops or restarts all running telemetry services.
* ``opt-in``:
Opts in to telemetry, and starts telemetry services. The opt-out file
``/etc/telemetrics/opt-out`` is removed.
Opts in to telemetry and the opt-in file ``/etc/telemetrics/opt-in``
is created. Note: this is a one time required operation before
telemetry can be used the first time.
* ``opt-out``:
Opts out of telemetry, and stops telemetry services. The opt-out file
``/etc/telemetrics/opt-out`` is created.
Opts out of telemetry, and stops telemetry services. The opt-in file
``/etc/telemetrics/opt-in`` is deleted.
* ``is-active``:
Checks if telemetry client daemons are active (telemprobd and telempostd).
@@ -52,6 +53,6 @@ RETURN VALUES
SEE ALSO
========
* ``telemd``\(1)
* ``telemprobd``\(1)
* https://github.com/clearlinux/telemetrics-client
* https://clearlinux.org/documentation/
+8 -2
View File
@@ -45,6 +45,8 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
\fBvoid tm_free_record(struct telem_ref *t_ref)\fP
.sp
\fBint tm_set_config_file(const char *c_file)\fP
.sp
\fBint tm_is_opted_in(void)\fP
.SH DESCRIPTION
.sp
The functions in the telemetry library facilitate the delivery of
@@ -60,15 +62,19 @@ The function \fBtm_set_payload()\fP attaches the provided telemetry record
data to the telemetry record. The current maximum payload size is 8192b.
.sp
The function \fBtm_send_record()\fP delivers the record to the local
\fBtelemd\fP(1) service.
\fBtelemprobd\fP(1) service.
.sp
The function \fBtm_set_config_file()\fP can be used to provide an alternate
configuration path to the telemetry library.
.sp
\fBtm_is_opted_in\fP is a utility provided to check if the one time opt\-in
has been performed.
.SH RETURN VALUES
.sp
All these functions return \fB0\fP on success, or a non\-zero return value
if an error occurred. The function \fBtm_free_record()\fP does not return
any value.
any value. \fBtm_is_opted_in\fP returns \fB1\fP when telemetry is opted\-in
otherwise \fB0\fP\&.
.SH SEE ALSO
.INDENT 0.0
.IP \(bu 2
+7 -2
View File
@@ -27,6 +27,8 @@ SYNOPSIS
``int tm_set_config_file(const char *c_file)``
``int tm_is_opted_in(void)``
DESCRIPTION
===========
@@ -44,18 +46,21 @@ The function ``tm_set_payload()`` attaches the provided telemetry record
data to the telemetry record. The current maximum payload size is 8192b.
The function ``tm_send_record()`` delivers the record to the local
``telemd``\(1) service.
``telemprobd``\(1) service.
The function ``tm_set_config_file()`` can be used to provide an alternate
configuration path to the telemetry library.
``tm_is_opted_in`` is a utility provided to check if the one time opt-in
has been performed.
RETURN VALUES
=============
All these functions return ``0`` on success, or a non-zero return value
if an error occurred. The function ``tm_free_record()`` does not return
any value.
any value. ``tm_is_opted_in`` returns ``1`` when telemetry is opted-in
otherwise ``0``.
SEE ALSO
+3
View File
@@ -0,0 +1,3 @@
fuzz_libtelem
fuzz_config
fuzz_iorec
+54
View File
@@ -0,0 +1,54 @@
bin_PROGRAMS = fuzz_libtelem fuzz_config fuzz_iorec
CC = clang
CFLAGS = \
-g \
-I../. \
-I../src/. \
-DLOCALSTATEDIR=\"/tmp\" \
-DDATADIR="" \
-DBACKEND_ADDR=\"abcde\"
# Define the source files for each binary
fuzz_libtelem_SOURCES = \
fuzz_libtelem.c
fuzz_config_SOURCES = \
../src/configuration.c \
../src/nica/hashmap.c \
../src/nica/inifile.c \
utils.c \
fuzz_config.c
fuzz_iorec_SOURCES = \
utils.c \
fuzz_iorec.c
fuzz_iorec_CFLAGS = \
$(CFLAGS) \
-fsanitize=address,fuzzer
fuzz_libtelem_CFLAGS = \
$(CFLAGS) \
-fsanitize=address,fuzzer \
-ltelemetry
fuzz_config_CFLAGS = \
$(CFLAGS) \
-fsanitize=fuzzer # There's a false positive in configuration.c:189:56
all: $(bin_PROGRAMS)
fuzz_libtelem: $(fuzz_libtelem_SOURCES)
$(CC) $(fuzz_libtelem_CFLAGS) -o $@ $^
fuzz_config: $(fuzz_config_SOURCES)
$(CC) $(fuzz_config_CFLAGS) -o $@ $^
fuzz_iorec: $(fuzz_iorec_SOURCES)
$(CC) $(fuzz_iorec_CFLAGS) -o $@ $^
clean:
rm -f $(bin_PROGRAMS) *.o
.PHONY: all clean
+21
View File
@@ -0,0 +1,21 @@
# Fuzzing telemetrics-client
## Build
```
make
```
## Run
```
#fuzz_config
fuzz_config -close_fd_mask=2
#fuzz_libtelem
fuzz_libtelem
```
# Link runs in background
```
sudo systemd-run --unit=fuzzing.service -r --uid=$(id -u) \
--working-directory=${PWD}/fuzz -E LD_LIBRARY_PATH=${PWD}/src/.libs/ fuzz/<fuzzer_> -max_total_time=86400 -runs=10000000 fuzz/corpus
```
+32
View File
@@ -0,0 +1,32 @@
#include <configuration.h>
#include <string.h>
#include <unistd.h>
#include "utils.h"
void test_configuration(const uint8_t *data, size_t size) {
char content[16384];
char config_file[] = "/tmp/config_fuzzerXXXXXX";
static struct configuration config = { { 0 }, { 0 }, { 0 }, false, NULL };
// Get a null ended string
strncpy(content, (const char *)data, size);
save_data(config_file, content);
read_config_from_file(config_file, &config);
for (int i = 0; i < 6; i++) {
if (config.strValues[i] == NULL) {
continue;
}
}
unlink(config_file);
}
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
test_configuration(data, size);
return 0;
}
+28
View File
@@ -0,0 +1,28 @@
#include <configuration.h>
#include <unistd.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include "utils.h"
void test_read_record(const uint8_t *data, size_t size) {
char content[16384];
char record_file[] = "/tmp/record_fuzzerXXXXXX";
static struct configuration config = { { 0 }, { 0 }, { 0 }, false, NULL };
// Get a null ended string
strncpy(content, (const char *)data, size);
save_data(record_file, content);
// read_record(char *fullpath, char *headers[], char **body, char **cfg_file)
unlink(record_file);
}
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
test_read_record(data, size);
return 0;
}
+73
View File
@@ -0,0 +1,73 @@
#include <telemetry.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
int test_tm_set_payload(char *payload) {
int ret = 0;
static uint32_t severity = 1;
static uint32_t payload_version = 1;
static const char *opt_class = "clearlinux/debug/fuzzing";
struct telem_ref *t_ref = NULL;
if ((ret = tm_create_record(&t_ref, (uint32_t)severity,
(char *)opt_class, payload_version)) < 0) {
return ret;
}
ret = tm_set_payload(t_ref, payload);
tm_free_record(t_ref);
return ret;
}
void test_tm_set_config_file(char *path) {
/*
No need to check returning value because
the important part is not it fails, but
if this crashes.
*/
tm_set_config_file(path);
}
int test_tm_set_event_id(char *full_id) {
int ret = 0;
char *id = NULL;
static uint32_t severity = 1;
static uint32_t payload_version = 1;
static const char *opt_class = "clearlinux/debug/fuzzing";
struct telem_ref *t_ref = NULL;
if ((ret = tm_create_record(&t_ref, (uint32_t)severity,
(char *)opt_class, payload_version)) < 0) {
return ret;
}
id = strndup(full_id, 32);
ret = tm_set_event_id(t_ref, id);
free(id);
tm_free_record(t_ref);
return ret;
}
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
int ret = 0;
char *payload = NULL;
/*
tm_set_payload expects a null terminated string, this
step duplicates data into a null terminated string.
*/
payload = strndup((char *)data, size);
if (payload == NULL) {
goto end;
}
test_tm_set_payload(payload);
test_tm_set_config_file(payload);
test_tm_set_event_id(payload);
free(payload);
end:
return ret;
}
+30
View File
@@ -0,0 +1,30 @@
COMPILER = clang
CLANG_FLAGS = \
-g \
-fsanitize=address,fuzzer
SOURCES = fuzz/libtelem_fuzzer.c
BIN = fuzz/libtelem_fuzzer
HALF_DAY_SEC = 43200
FUZZ_FLAGS = \
-max_len=16384 \
-timeout=10 \
-only_ascii=1 \
-runs=1000000 \
-max_total_time=$(HALF_DAY_SEC)
build_fuzz_libtelem:
LD_LIBRARY_PATH=src/.libs $(COMPILER) $(CLANG_FLAGS) -ltelemetry $(SOURCES) -o $(BIN)
fuzz: build_fuzz_kibtelem
$(BIN) fuzz/corpus $(FUZZ_FLAGS)
# vim: filetype=automake tabstop=8 shiftwidth=8 noexpandtab
create a makefile that builds {3 binaries}
{4 binaries}: fuzz_libtelem fuzz_config fuzz_iorec
each one has different compiling flags and SOURCES
+24
View File
@@ -0,0 +1,24 @@
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int save_data(char *filename, const char *data) {
FILE *temp_file = NULL;
int fd;
fd = mkstemp(filename);
if (fd == -1) {
return 1;
}
temp_file = fdopen(fd, "w");
if (temp_file == NULL) {
close(fd);
return 1;
}
fprintf(temp_file, "%s", data);
fclose(temp_file);
return 0;
}
+1
View File
@@ -0,0 +1 @@
int save_data(char *filename, const char *data);
-3
View File
@@ -1,3 +0,0 @@
dist_bin_SCRIPTS = %D%/telemctl
# vim: filetype=automake tabstop=8 shiftwidth=8 noexpandtab
-150
View File
@@ -1,150 +0,0 @@
#!/bin/bash
declare -a SPECIAL_UNITS=(
hprobe.timer
telemprobd.socket
telempostd.path
klogscanner.service
journal-probe-tail.service
python-probe.path
)
declare -a SERVICES=(
hprobe.service
pstore-probe.service
telemprobd.service
telempostd.service
journal-probe.service
)
SCRIPT="$0"
TELEM_DIR=/etc/telemetrics
OPT_OUT_FILE=${TELEM_DIR}/opt-out
TELEM_WRK_DIRS_CONF=/usr/lib/tmpfiles.d/telemetrics-dirs.conf
create_work_dirs() {
# Creates dirs if missing, adjust ownership if exists
systemd-tmpfiles --create ${TELEM_WRK_DIRS_CONF}
}
telem_remove_work_dirs() {
# Remove dirs
awk '/^d/{print $2}' ${TELEM_WRK_DIRS_CONF} | xargs rm -rf;
}
exit_ok() {
echo "$1" > /dev/stderr
exit 0
}
exit_err() {
echo "$1" > /dev/stderr
exit 1
}
notice() {
echo "$1" > /dev/stderr
}
for_each_service() {
local action=$1 && shift
local -a array=($*)
for service in "${array[@]}"; do
systemctl $action $service
[ $? -ne 0 ] && notice "Failed to $action ${service}. Continuing..."
done
}
telem_stop() {
# the special units must be stopped first so that activation no longer happens
for_each_service "stop" ${SPECIAL_UNITS[@]}
for_each_service "stop" ${SERVICES[@]}
}
telem_start() {
[ -f $OPT_OUT_FILE ] && exit_err "Opt out is enabled. Cannot start services."
# trigger systemd-tmpfiles work dirs creation
create_work_dirs
# the units in SERVICES are activated as needed, so no need to start them
for_each_service "start" ${SPECIAL_UNITS[@]}
}
telem_is_active() {
# check only activation units
echo "telemprobd :" $(systemctl is-active telemprobd.socket)
echo "telempostd :" $(systemctl is-active telempostd.path)
}
telem_opt_out() {
[ -f $OPT_OUT_FILE ] && exit_ok "Already opted out. Nothing to do."
mkdir -p $TELEM_DIR || exit_err "Failed to create ${TELEM_DIR}."
touch $OPT_OUT_FILE || exit_err "Failed to create ${OPT_OUT_FILE}."
telem_stop
telem_remove_work_dirs
}
telem_opt_in() {
[ ! -f $OPT_OUT_FILE ] && exit_ok "Already opted in. Nothing to do."
rm -f $OPT_OUT_FILE || exit_err "Failed to remove ${OPT_OUT_FILE}."
telem_start
}
telem_restart() {
telem_stop
telem_start
}
telem_journal_cli() {
telem_journal "$@"
}
usage() {
format=' %-10s %s\n'
printf "\n"
printf "%s - Control actions for telemetry services\n" "$SCRIPT"
printf "\n"
printf "$format" "stop" "Stops all running telemetry services"
printf "$format" "start" "Starts all telemetry services"
printf "$format" "restart" "Restarts all telemetry services"
printf "$format" "is-active" "Checks if telemprobd and telempostd are 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 telemtry journal contents. Use -h argument with"
printf "$format" "" "command for more options"
printf "\n"
exit 2
}
if [ "$1" != "journal" ] && [ $# -ne 1 ]; then
usage
fi
if [ $EUID -ne 0 ]; then
exit_err "Must be root to run this command. Exiting..."
fi
SUBCOMMAND=$1
case $SUBCOMMAND in
opt-out)
telem_opt_out ;;
opt-in)
telem_opt_in ;;
stop)
telem_stop ;;
start)
telem_start ;;
restart)
telem_restart ;;
is-active)
telem_is_active ;;
journal)
telem_journal_cli "$@" ;;
*)
notice "Unknown command passed to $SCRIPT"
usage ;;
esac
exit 0
# vi: ts=8 sw=2 sts=2 et tw=80
+7 -1
View File
@@ -75,7 +75,7 @@ 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"
#define TM_OPT_OUT_FILE "/etc/telemetrics/opt-out"
#define TM_OPT_IN_FILE "/etc/telemetrics/opt-in"
/* Currently max supported payload size is 8kb */
#define MAX_PAYLOAD_LENGTH 8192
@@ -102,6 +102,11 @@ static const uint32_t RECORD_FORMAT_VERSION = 4;
/* Maximum records that can be processed in a single spool run loop*/
#define TM_SPOOL_MAX_PROCESS_RECORDS 60
/* Definitions for config file override */
#define CFG_PREFIX "CFG:"
#define CFG_PREFIX_LENGTH 4
#define CFG_PREFIX_32BIT 0x3a474643
/* Very simple structure. Array of header strings and a payload. Calling
* program is reponsible for passing in the payload as a simple string.
*/
@@ -114,4 +119,5 @@ struct telem_record {
const char *get_header_name(int ind);
/* vi: set ts=8 sw=8 sts=4 et tw=80 cino=(0: */
+130 -58
View File
@@ -28,6 +28,7 @@
#include "configuration.h"
#include "util.h"
#include "common.h"
#include "log.h"
#include "nica/inifile.h"
@@ -38,17 +39,46 @@ static NcHashmap *keyfile = NULL;
static bool cmd_line_cfg = false;
/* Conf strings, integers, and booleans expected in the conf file */
const char *config_key_str[] = { NULL, "server", "socket_path", "spool_dir",
"rate_limit_strategy", "cainfo",
"tidheader", NULL };
static const char *config_key_str[] = { "server",
"socket_path",
"spool_dir",
"rate_limit_strategy",
"cainfo",
"tidheader"};
const char *config_key_int[] = { NULL, "record_expiry", "spool_max_size",
"spool_process_time", "record_window_length",
"byte_window_length", "record_burst_limit",
"byte_burst_limit", NULL };
static const char *config_key_int[] = { "record_expiry",
"spool_max_size",
"spool_process_time",
"record_window_length",
"byte_window_length",
"record_burst_limit",
"byte_burst_limit" };
static const char *config_key_bool[] = { "rate_limit_enabled",
"daemon_recycling_enabled",
"record_retention_enabled",
"record_server_delivery_enabled" };
static const char *config_str_default[] = { DEFAULT_SERVER_ADDR,
DEFAULT_SOCKET_PATH,
DEFAULT_SPOOL_DIR,
DEFAULT_RATE_LIMIT_STRATEGY,
DEFAULT_CAINFO,
DEFAULT_TIDHEADER };
static const bool config_bool_default[] = { DEFAULT_RATE_LIMIT_ENABLED,
DEFAULT_DAEMON_RECYCLING_ENABLED,
DEFAULT_RECORD_RETENTION_ENABLED,
DEFAULT_RECORD_SERVER_DELIVERY_ENABLED };
static const int config_int_default[] = { DEFAULT_RECORD_EXPIRY,
DEFAULT_SPOOL_MAX_SIZE,
DEFAULT_SPOOL_PROCESS_TIME,
DEFAULT_RECORD_WINDOW_LENGTH,
DEFAULT_BYTE_WINDOW_LENGTH,
DEFAULT_RECORD_BURST_LIMIT,
DEFAULT_BYTE_BURST_LIMIT };
const char *config_key_bool[] = { NULL, "rate_limit_enabled", "daemon_recycling_enabled",
"record_retention_enabled", "record_server_delivery_enabled", NULL };
static struct configuration config = { { 0 }, { 0 }, { 0 }, false, NULL };
@@ -73,6 +103,19 @@ static int validate_config_file(const char *f)
}
const char *get_config_file(void)
{
return config_file;
}
const char *get_cmd_line_config_file(void)
{
if (cmd_line_cfg == true) {
return config_file;
}
return NULL;
}
int set_config_file(const char *filename)
{
int ret;
@@ -90,6 +133,37 @@ int set_config_file(const char *filename)
return ret;
}
bool set_default_config_values(struct configuration *config)
{
for (int i = 0; i < CONF_STR_MAX; i++) {
config->strValues[i] = strdup(config_str_default[i]);
if (config->strValues[i] == NULL) {
telem_log(LOG_ERR, "Could not set config item %s: %s\n",
config_key_str[i], strerror(errno));
return false;
}
}
for (int i = 0; i < CONF_INT_MAX; i++) {
config->intValues[i] = config_int_default[i];
}
for (int i = 0; i < CONF_BOOL_MAX; i++) {
config->boolValues[i] = config_bool_default[i];
}
return true;
}
/* Needed for unit testing */
void free_config_struct(struct configuration *config)
{
if (keyfile != NULL) {
nc_hashmap_free(keyfile);
}
for (int i = 0; i < CONF_STR_MAX; i++) {
free(config->strValues[i]);
}
}
bool read_config_from_file(char *config_file, struct configuration *config)
{
if (keyfile != NULL) {
@@ -98,73 +172,64 @@ bool read_config_from_file(char *config_file, struct configuration *config)
keyfile = nc_ini_file_parse(config_file);
if (!keyfile) {
#ifdef DEBUG
fprintf(stderr, "ERR: Failed to read config file\n");
#endif
telem_log(LOG_ERR, "Failed to read config file\n");
return false;
} else {
for (int i = CONF_STR_MIN + 1; i < CONF_STR_MAX; i++) {
for (int i = 0; i < CONF_STR_MAX; i++) {
char *ptr;
ptr = nc_hashmap_get(nc_hashmap_get(keyfile, "settings"), config_key_str[i]);
if (ptr) {
config->strValues[i] = strdup(ptr);
if (config->strValues[i] == NULL) {
telem_log(LOG_ERR, "Could not set config item %s: %s\n",
config_key_str[i], strerror(errno));
return false;
}
} else {
fprintf(stderr, "ERR: missing key with string value: %s\n", config_key_str[i]);
return false;
config->strValues[i] = strdup(config_str_default[i]);
}
}
for (int i = CONF_INT_MIN + 1; i < CONF_INT_MAX; i++) {
for (int i = 0; i < CONF_INT_MAX; i++) {
char *ptr;
ptr = nc_hashmap_get(nc_hashmap_get(keyfile, "settings"), config_key_int[i]);
if (ptr) {
errno = 0;
config->intValues[i] = strtoll(ptr, NULL, 10);
if (errno != 0) {
telem_log(LOG_ERR, "Error while parsing value of option %s: %s\n",
config_key_int[i], strerror(errno));
return false;
}
} else {
fprintf(stderr, "ERR: missing key with integer value: %s\n", config_key_int[i]);
return false;
config->intValues[i] = config_int_default[i];
}
}
for (int i = CONF_BOOL_MIN + 1; i < CONF_BOOL_MAX; i++) {
for (int i = 0; i < CONF_BOOL_MAX; i++) {
char *ptr;
ptr = nc_hashmap_get(nc_hashmap_get(keyfile, "settings"), config_key_bool[i]);
config->boolValues[i] = false;
if (ptr) {
if (strcasecmp(ptr, "TRUE") == 0) {
if ((strcasecmp(ptr, "TRUE") == 0) || (strcmp(ptr, "1") == 0)) {
config->boolValues[i] = true;
}
if (strcasecmp(ptr, "1") == 0) {
config->boolValues[i] = true;
}
} else {
/* New configuration keys, CONF_RECORD_RETENTION_ENABLED and CONF_RECORD_SERVER_DELIVERY_ENABLED
* 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 if ((strcasecmp(ptr, "FALSE") == 0) || (strcmp(ptr, "0") == 0)) {
config->boolValues[i] = false;
} else {
fprintf(stderr, "ERR: missing key with boolean value: %s\n", config_key_bool[i]);
telem_log(LOG_ERR, "Configuration item '%s' requires a boolean value\n",
config_key_bool[i]);
return false;
}
} else {
config->boolValues[i] = config_bool_default[i];
}
}
}
config->initialized = true;
return true;
}
void initialize_config(void)
static void initialize_config(void)
{
if (config.initialized) {
return;
@@ -175,26 +240,25 @@ void initialize_config(void)
if (access(etc_config_file, R_OK) == 0) {
config_file = etc_config_file;
} else {
if (access(default_config_file,
R_OK) == 0) {
if (access(default_config_file, R_OK) == 0) {
config_file = default_config_file;
} else {
/* If there is no default config, exit with failure */
#ifdef DEBUG
fprintf(stderr, "ERR: No configuration file"
" found, exiting\n");
#endif
exit(EXIT_FAILURE);
}
}
}
if (!read_config_from_file(config_file, &config)) {
/* Error while parsing file */
#ifdef DEBUG
fprintf(stderr, "ERR: Error while parsing configuration file\n");
#endif
exit(EXIT_FAILURE);
if (config_file) {
if (!read_config_from_file(config_file, &config)) {
/* Error while parsing file */
exit(EXIT_FAILURE);
}
}
else {
if (!set_default_config_values(&config)) {
exit(EXIT_FAILURE);
}
}
config.initialized = true;
}
void reload_config(void)
@@ -214,7 +278,7 @@ void free_configuration(void)
return;
}
for (int i = CONF_STR_MIN + 1; i < CONF_STR_MAX; i++) {
for (int i = 0; i < CONF_STR_MAX; i++) {
free(config.strValues[i]);
}
@@ -223,6 +287,14 @@ void free_configuration(void)
}
}
/* Needed for unit testing */
void free_config_file(void)
{
if (config_file) {
free(config_file);
}
}
const char *server_addr_config()
{
initialize_config();
+33 -8
View File
@@ -18,13 +18,31 @@
#include <stdbool.h>
#include <stdint.h>
/* Default configuration settings */
#define DEFAULT_SERVER_ADDR BACKEND_ADDR
#define DEFAULT_SOCKET_PATH "/run/telem-0"
#define DEFAULT_SPOOL_DIR LOCALSTATEDIR "/spool/telemetry"
#define DEFAULT_RATE_LIMIT_STRATEGY "spool"
#define DEFAULT_CAINFO ""
#define DEFAULT_TIDHEADER "X-Telemetry-TID: 6907c830-eed9-4ce9-81ae-76daf8d88f0f"
#define DEFAULT_RECORD_EXPIRY 1200
#define DEFAULT_SPOOL_MAX_SIZE 5120
#define DEFAULT_SPOOL_PROCESS_TIME 120
#define DEFAULT_RECORD_WINDOW_LENGTH 15
#define DEFAULT_BYTE_WINDOW_LENGTH 20
#define DEFAULT_RECORD_BURST_LIMIT 1000
#define DEFAULT_BYTE_BURST_LIMIT -1
#define DEFAULT_RATE_LIMIT_ENABLED true
#define DEFAULT_DAEMON_RECYCLING_ENABLED true
#define DEFAULT_RECORD_RETENTION_ENABLED false
#define DEFAULT_RECORD_SERVER_DELIVERY_ENABLED true
#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 {
CONF_STR_MIN = 0,
CONF_SERVER_ADDR,
CONF_SERVER_ADDR = 0,
CONF_SOCKET_PATH,
CONF_SPOOL_DIR,
CONF_RATE_LIMIT_STRATEGY,
@@ -34,8 +52,7 @@ enum config_str_keys {
};
enum config_int_keys {
CONF_INT_MIN = 0,
CONF_RECORD_EXPIRY,
CONF_RECORD_EXPIRY = 0,
CONF_SPOOL_MAX_SIZE,
CONF_SPOOL_PROCESS_TIME,
CONF_RECORD_WINDOW_LENGTH,
@@ -46,8 +63,7 @@ enum config_int_keys {
};
enum config_bool_keys {
CONF_BOOL_MIN = 0,
CONF_RATE_LIMIT_ENABLED,
CONF_RATE_LIMIT_ENABLED = 0,
CONF_DAEMON_RECYCLING_ENABLED,
CONF_RECORD_RETENTION_ENABLED,
CONF_RECORD_SERVER_DELIVERY_ENABLED,
@@ -65,6 +81,15 @@ typedef struct configuration {
/* Sets the configuration file to be used later */
int set_config_file(const char *filename);
/* Gets the configuration currently in use */
const char *get_config_file(void);
/* Gets the configuration specified via command line or NULL */
const char *get_cmd_line_config_file(void);
/* Sets all default configuration values to a given config */
bool set_default_config_values(struct configuration *config);
/* Parses the ini format config file */
bool read_config_from_file(char *filename, struct configuration *config);
-13
View File
@@ -1,13 +0,0 @@
[Unit]
Description=Telemetrics BERT Probe
Requires=telemprobd.socket
After=telemprobd.socket
ConditionPathExists=!/etc/telemetrics/opt-out
ConditionPathExists=/sys/firmware/acpi/tables/data/BERT
[Service]
ExecStart=@bindir@/bertprobe
User=telemetry
[Install]
WantedBy=multi-user.target
+4
View File
@@ -0,0 +1,4 @@
[settings]
server=http://127.0.0.1
cainfo=/tmp/cacert.crt
+1 -1
View File
@@ -2,7 +2,7 @@
Description=Telemetrics Heartbeat Probe
Requires=telemprobd.socket
After=telemprobd.socket
ConditionPathExists=!/etc/telemetrics/opt-out
ConditionPathExists=/etc/telemetrics/opt-in
[Service]
ExecStart=@bindir@/hprobe -Hlu
+2 -1
View File
@@ -1,10 +1,11 @@
[Unit]
Description=Daily Heartbeat for Telemetrics
ConditionPathExists=!/etc/telemetrics/opt-out
ConditionPathExists=/etc/telemetrics/opt-in
[Timer]
OnCalendar=daily
AccuracySec=12h
RandomizedDelaySec=21600
Persistent=true
[Install]
+1 -1
View File
@@ -2,7 +2,7 @@
Description=Telemetrics Systemd Journal Probe
Requires=telemprobd.socket
After=telemprobd.socket
ConditionPathExists=!/etc/telemetrics/opt-out
ConditionPathExists=/etc/telemetrics/opt-in
[Service]
ExecStart=@bindir@/journalprobe -t
+1 -1
View File
@@ -2,7 +2,7 @@
Description=Telemetrics Systemd Journal Probe
Requires=telemprobd.socket
After=telemprobd.socket
ConditionPathExists=!/etc/telemetrics/opt-out
ConditionPathExists=/etc/telemetrics/opt-in
[Service]
ExecStart=@bindir@/journalprobe
+1
View File
@@ -1,5 +1,6 @@
[Unit]
Description=Telemetrics Kernel Log Scanner
ConditionVirtualization=!container
Requires=telemprobd.socket
After=telemprobd.socket
+3 -19
View File
@@ -5,20 +5,19 @@ pathfix = @sed \
-e 's|@localstatedir[@]|$(localstatedir)|g' \
-e 's|@PACKAGE_VERSION[@]|$(PACKAGE_VERSION)|g' \
-e 's|@SOCKETDIR[@]|$(SOCKETDIR)|g' \
-e 's|@systemctldir[@]|$(SYSTEMD_SYSTEMCTLDIR)|g'
-e 's|@systemctldir[@]|$(SYSTEMD_SYSTEMCTLDIR)|g' \
-e 's|@BACKEND_ADDR[@]|$(BACKEND_ADDR)|g'
EXTRA_DIST += \
%D%/40-core-ulimit.conf \
%D%/40-crash-probe.conf.in \
%D%/example.conf \
%D%/example.1.conf \
%D%/example.2.conf \
%D%/hprobe.service.in \
%D%/hprobe.timer \
%D%/bert-probe.service.in \
%D%/journal-probe.service.in \
%D%/journal-probe-tail.service.in \
%D%/python-probe.service.in \
%D%/python-probe.path.in \
%D%/pstore-probe.service.in \
%D%/klogscanner.service.in \
%D%/pstore-clean.service.in \
@@ -53,11 +52,8 @@ systemdunitdir = @SYSTEMD_UNITDIR@
systemdunit_DATA = \
%D%/hprobe.service \
%D%/hprobe.timer \
%D%/bert-probe.service \
%D%/journal-probe.service \
%D%/journal-probe-tail.service \
%D%/python-probe.service \
%D%/python-probe.path \
%D%/pstore-probe.service \
%D%/klogscanner.service \
%D%/pstore-clean.service \
@@ -70,9 +66,6 @@ systemdunit_DATA = \
%D%/hprobe.service: %D%/hprobe.service.in
$(pathfix) < $< > $@
%D%/bert-probe.service: %D%/bert-probe.service.in
$(pathfix) < $< > $@
%D%/journal-probe.service: %D%/journal-probe.service.in
$(pathfix) < $< > $@
@@ -94,12 +87,6 @@ systemdunit_DATA = \
%D%/telempostd.service: %D%/telempostd.service.in
$(pathfix) < $< > $@
%D%/python-probe.service: %D%/python-probe.service.in
$(pathfix) < $< > $@
%D%/python-probe.path: %D%/python-probe.path.in
$(pathfix) < $< > $@
%D%/telemprobd.service: %D%/telemprobd.service.in
$(pathfix) < $< > $@
@@ -123,8 +110,6 @@ clean-local:
%D%/telemprobd.socket \
%D%/telempostd.service \
%D%/telempostd.path \
%D%/python-probe.service \
%D%/python-probe.path \
%D%/telemprobd-update-trigger.service \
%D%/telemetrics.conf \
%D%/telemetrics-dirs.conf \
@@ -136,5 +121,4 @@ clean-local:
%D%/klogscanner.service \
%D%/pstore-clean.service \
%D%/hprobe.service \
%D%/bert-probe.service \
%D%/.dirstamp
+1
View File
@@ -2,6 +2,7 @@
Description=Telemetrics Pstore Probe
Requires=telemprobd.socket
After=telemprobd.socket
ConditionPathExists=/etc/telemetrics/opt-in
[Service]
ExecStart=@bindir@/pstoreprobe
-9
View File
@@ -1,9 +0,0 @@
[Unit]
Description=Python Probe Daemon Staging
ConditionPathExists=!/etc/telemetrics/opt-out
[Path]
DirectoryNotEmpty=@localstatedir@/lib/telemetry/python
[Install]
WantedBy=multi-user.target
-13
View File
@@ -1,13 +0,0 @@
[Unit]
Description=Telemetrics Python Probe
Requires=telemprobd.socket
After=telemprobd.socket
ConditionPathExists=!/etc/telemetrics/opt-out
[Service]
User=telemetry
ExecStart=@bindir@/pythonprobe
[Install]
WantedBy=multi-user.target
-1
View File
@@ -1,5 +1,4 @@
d @localstatedir@/lib/telemetry 0755 telemetry telemetry -
d @localstatedir@/lib/telemetry/python 01777 telemetry telemetry -
d @localstatedir@/spool/telemetry 0750 telemetry telemetry -
d @localstatedir@/log/telemetry 0750 telemetry telemetry -
d @localstatedir@/log/telemetry/records 0750 telemetry telemetry -
+19 -17
View File
@@ -1,67 +1,69 @@
# values represent their default state
# copy to /etc/telemetrics/telemetrics.conf and modify for user config
[settings]
# ip address of destination server for record delivery
server=https://clr.telemetry.intel.com/v2/collector
#server=@BACKEND_ADDR@
socket_path=@SOCKETDIR@/telem-0
#socket_path=@SOCKETDIR@/telem-0
# certificate file to use to validate ssl endpoint
cainfo=
#cainfo=
# Telemetry id - post header used to group records in ingestion service,
# which may be ingesting for more than one set of clients. Can be set
# to any string.
tidheader=X-Telemetry-TID:\s6907c830-eed9-4ce9-81ae-76daf8d88f0f
#tidheader=X-Telemetry-TID:\s6907c830-eed9-4ce9-81ae-76daf8d88f0f
# record expiry time in minutes
record_expiry=1200
#record_expiry=1200
spool_dir=@localstatedir@/spool/telemetry
#spool_dir=@localstatedir@/spool/telemetry
# maximum size of the spool directory in KB, -1 = quota disabled.
# The block size of the files in this directory is considered,
# and not the actual size of the record itself.
spool_max_size=5120
#spool_max_size=5120
# time in seconds for processing spool
# Valid range: 120..300. Values outside this range are clamped.
spool_process_time=120
#spool_process_time=120
# rate limit enabled - if this is set to false then all rate-limiting disabled.
# It is possible to disable each rate-limit individually below.
rate_limit_enabled=true
#rate_limit_enabled=true
# rate limiting record burst limit
# Valid Range: 0..INT_MAX, -1 = disabled.
record_burst_limit=1000
#record_burst_limit=1000
# rate limiting record window length in minutes
# Valid Range: 0..59
record_window_length=15
#record_window_length=15
# rate limiting byte burst limit
# Valid Range: 0..INT_MAX, -1 = disabled.
byte_burst_limit=-1
#byte_burst_limit=-1
# rate limiting byte window length in minutes
# Valid Range: 0..59
byte_window_length=20
#byte_window_length=20
# rate limit strategy - what to do with record if rate-limiting prevents
# delivery over network
# Valid stategies: spool, drop
rate_limit_strategy=spool
#rate_limit_strategy=spool
# daemon recycling enabled - if daemon has been running for a while (2 hours),
# has not any client nor spool data, then it exits.
# 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_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
#record_retention_enabled=false
+1 -1
View File
@@ -1,6 +1,6 @@
[Unit]
Description=Telemetry Post Daemon staging
ConditionPathExists=!/etc/telemetrics/opt-out
ConditionPathExists=/etc/telemetrics/opt-in
[Path]
DirectoryNotEmpty=@localstatedir@/spool/telemetry
+1 -1
View File
@@ -1,6 +1,6 @@
[Unit]
Description=Telemetrics Post Daemon
ConditionPathExists=!/etc/telemetrics/opt-out
ConditionPathExists=/etc/telemetrics/opt-in
[Service]
ExecStart=@bindir@/telempostd
+1 -1
View File
@@ -1,6 +1,6 @@
[Unit]
Description=Telemetrics Daemon
ConditionPathExists=!/etc/telemetrics/opt-out
ConditionPathExists=/etc/telemetrics/opt-in
[Service]
ExecStart=@bindir@/telemprobd
+1 -1
View File
@@ -1,6 +1,6 @@
[Unit]
Description=Telemetrics Daemon
ConditionPathExists=!/etc/telemetrics/opt-out
ConditionPathExists=/etc/telemetrics/opt-in
[Socket]
ListenStream=@SOCKETDIR@/telem-0
+40 -59
View File
@@ -41,69 +41,19 @@ bool _fgets(char *s, int n, FILE *stream)
return true;
}
void stage_record(char *filepath, char *headers[], char *body)
bool read_record(char *fullpath, char *headers[], char **body, char **cfg_file)
{
int tmpfd;
FILE *tmpfile = NULL;
// Use default path if not provided
if (filepath == NULL) {
telem_log(LOG_ERR, "filepath value must be provided, aborting\n");
exit(EXIT_FAILURE);
}
tmpfd = mkstemp(filepath);
if (!tmpfd) {
telem_perror("Error opening staging file");
close(tmpfd);
if (unlink(filepath)) {
telem_perror("Error deleting staging file");
}
goto clean_exit;
}
// open file
tmpfile = fdopen(tmpfd, "a");
if (!tmpfile) {
telem_perror("Error opening temp stage file");
close(tmpfd);
if (unlink(filepath)) {
telem_perror("Error deleting temp stage file");
}
goto clean_exit;
}
// write headers
for (int i = 0; i < NUM_HEADERS; i++) {
fprintf(tmpfile, "%s\n", headers[i]);
}
//write body
fprintf(tmpfile, "%s\n", body);
fflush(tmpfile);
fclose(tmpfile);
clean_exit:
return;
}
bool read_record(char *fullpath, char *headers[], char **body)
{
int i, ret = 0;
int i = 0;
bool result = false;
FILE *fp = NULL;
long offset;
char line[LINE_MAX] = { 0 };
struct stat buf;
#if (LINE_MAX > PATH_MAX)
char line[LINE_MAX+1] = { 0 };
#else
char line[PATH_MAX+1] = { 0 };
#endif
long size;
ret = stat(fullpath, &buf);
if (ret == -1) {
telem_log(LOG_ERR, "Unable to stat record %s in staging\n", fullpath);
return false;
}
size = buf.st_size;
uint32_t cfg_prefix = 0;
fp = fopen(fullpath, "r");
if (fp == NULL) {
@@ -111,9 +61,40 @@ bool read_record(char *fullpath, char *headers[], char **body)
return false;
}
// Get the file size
fseek(fp, 0 , SEEK_END);
size = ftell(fp);
fseek(fp, 0 , SEEK_SET);
// First line may contain configuration file path
if (fread(&cfg_prefix, CFG_PREFIX_LENGTH, 1, fp) != 1) {
telem_log(LOG_ERR, "Error while parsing staged record configuration info.\n");
goto read_error;
}
if (cfg_prefix == CFG_PREFIX_32BIT) {
if (!_fgets(line, sizeof(line), fp)) {
telem_log(LOG_ERR, "Error while parsing staged record [%x]\n", cfg_prefix);
goto read_error;
}
size_t pathlen = strlen(line);
*cfg_file = malloc(pathlen + 1);
if (*cfg_file == NULL) {
telem_log(LOG_ERR, "Could not allocate memory for config file path\n");
goto read_error;
}
strcpy (*cfg_file, line);
telem_debug("DEBUG: cfg_file specified: %s\n", *cfg_file);
} else {
telem_debug("DEBUG: no user cfg file specified, cfg_prefix: %08x\n", cfg_prefix);
*cfg_file = NULL;
rewind(fp);
}
for (i = 0; i < NUM_HEADERS; i++) {
const char *header_name = get_header_name(i);
if (!_fgets(line, LINE_MAX, fp)) {
if (!_fgets(line, sizeof(line), fp)) {
telem_log(LOG_ERR, "Error while parsing staged record\n");
fclose(fp);
return false;
+1 -11
View File
@@ -16,16 +16,6 @@
#include <stdbool.h>
/**
* Save a telemetry record to disk
*
* @param path pointer to a directory to save record
* @param headers pointer to array of headers and values
* @param body record message content
*
*/
void stage_record(char *path, char *headers[], char *body);
/**
* Reads a telemetry record
*
@@ -35,4 +25,4 @@ void stage_record(char *path, char *headers[], char *body);
*
* @return true if successful otherwise false
*/
bool read_record(char *fullpath, char *headers[], char **body);
bool read_record(char *fullpath, char *headers[], char **body, char **cfg);
+12 -9
View File
@@ -24,14 +24,18 @@
#include "common.h"
#include "journal.h"
void print_usage(void)
static void print_usage(void)
{
printf(" Usage\n");
printf("Usage:\n");
printf(" telem_journal [-Vi] [-r <record_id>] [-e <event_id>] [-c <classification>] [-b <boot_id>]\n\n");
printf("Where:\n");
printf(" -r, --record_id Print record with specific record_id\n");
printf(" -e, --event_id Print records with specific event_id\n");
printf(" -c, --classification Print records with specific classification\n");
printf(" -b, --boot_id Print records with specific boot_id\n");
printf(" -i, --include_record Include record content\n");
printf(" -i, --include_record Include record content if available.\n");
printf(" Content only available when telemetry is configured\n");
printf(" with \"record_retention_enabled=true\"\n");
printf(" -V, --verbose Verbose output\n");
printf(" -h, --help Display this help message\n");
}
@@ -41,8 +45,8 @@ int main(int argc, char **argv)
int rc = EXIT_SUCCESS;
int count = 0;
int verbose_output = 0;
int record = 0;
bool verbose_output = false;
bool record = false;
char *boot_id = NULL;
char *record_id = NULL;
char *event_id = NULL;
@@ -78,16 +82,15 @@ int main(int argc, char **argv)
boot_id = optarg;
break;
case 'V':
verbose_output = 1;
verbose_output = true;
break;
case 'i':
record = 1;
record = true;
break;
case 'h':
print_usage();
exit(EXIT_SUCCESS);
case '?':
/** default */
default:
print_usage();
exit(EXIT_FAILURE);
}
+38 -69
View File
@@ -58,9 +58,7 @@ static int serialize_journal_entry(struct JournalEntry *entry, char **buff)
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
telem_log(LOG_ERR, "Error: Unable to serialize data in buff\n");
rc = -1;
}
@@ -101,11 +99,14 @@ static int deserialize_journal_entry(char *line, struct JournalEntry **entry)
size_t offset = 0;
struct JournalEntry *e = NULL;
if (line == NULL) {
/* Assume an error... */
*entry = NULL;
if (line == NULL || !strlen(line)) {
return -1;
}
e = malloc(sizeof(struct JournalEntry));
e = calloc(1, sizeof(struct JournalEntry));
if (!e) {
return -1;
}
@@ -140,10 +141,6 @@ static int deserialize_journal_entry(char *line, struct JournalEntry **entry)
e->boot_id = out;
rc = 0;
break;
default:
assert(i < 0 && i > 4);
free(out);
rc = 1;
}
} else {
rc = -1;
@@ -204,9 +201,7 @@ static int save_entry(FILE *fptr, struct JournalEntry *entry)
}
if ((rc = serialize_journal_entry(entry, &serialized_data)) == 0) {
#ifdef DEBUG
printf("Saving: %s\n", serialized_data);
#endif
telem_debug("DEBUG: Saving: %s\n", serialized_data);
fprintf(fptr, "%s\n", serialized_data);
fflush(fptr);
free(serialized_data);
@@ -229,9 +224,7 @@ static int read_boot_id(char buff[])
fs = fopen(BOOTID_FILE, "r");
if (!fs) {
#ifdef DEBUG
fprintf(stderr, "Unable to open %s for reading: %d\n", BOOTID_FILE, errno);
#endif
telem_log(LOG_ERR, "Error: Unable to open %s for reading: %d\n", BOOTID_FILE, errno);
return rc;
}
@@ -264,9 +257,7 @@ static int skip_n_lines(int n, FILE *fptr, int (*found_record)(char *))
struct JournalEntry *entry = NULL;
if (fptr == NULL) {
#ifdef DEBUG
fprintf(stderr, "fptr argument to skip_n_lines is invalid\n");
#endif
telem_log(LOG_ERR, "fptr argument to skip_n_lines is invalid\n");
// File descriptor in bad state
return EBADFD;
}
@@ -281,6 +272,8 @@ static int skip_n_lines(int n, FILE *fptr, int (*found_record)(char *))
break;
}
if (found_record != NULL) {
if (!strlen(line))
continue;
deserialize_journal_entry(line, &entry);
if (entry) {
found_record(entry->record_id);
@@ -327,9 +320,7 @@ static int copy_to_tmp(FILE *fptr, char *tmp_path)
if (fclose(fptr_tmp) != 0) {
rc = errno;
#ifdef DEBUG
perror("Error: ");
#endif
telem_perror("Error");
}
free(line);
@@ -350,22 +341,20 @@ TelemJournal *open_journal(const char *journal_file)
fptr = fopen(journal_file, "a+");
}
if (fptr == NULL) {
#ifdef DEBUG
perror("Error while opening journal file: ");
#endif
telem_perror("Error while opening journal file");
return NULL;
}
if (read_boot_id(boot_id) != 0) {
#ifdef DEBUG
perror("Error while reading boot_id: ");
#endif
telem_perror("Error while reading boot_id");
fclose(fptr);
return NULL;
}
telem_journal = malloc(sizeof(struct TelemJournal));
if (!telem_journal) {
fprintf(stderr, "Unable to allocate more memory\n");
telem_log(LOG_CRIT, "CRIT: Unable to allocate memory\n");
fclose(fptr);
return NULL;
}
@@ -379,9 +368,7 @@ TelemJournal *open_journal(const char *journal_file)
telem_journal->latest_record_id = NULL;
telem_journal->prune_entry_callback = NULL;
#ifdef DEBUG
printf("Records in db: %d\n", telem_journal->record_count);
#endif
telem_debug("Records in db: %d\n", telem_journal->record_count);
return telem_journal;
}
@@ -433,7 +420,7 @@ static void print_record(char *record_id)
recordfp = fopen(filepath, "r");
if (!recordfp) {
telem_perror("Error when opening a record to print");
telem_log(LOG_INFO, "Could not open record %s: %s\n", record_id, strerror(errno));
return;
}
@@ -476,11 +463,13 @@ int print_journal(TelemJournal *telem_journal, char *classification,
if (rc == -1) {
return rc;
} else if (rc != 0) {
fprintf(stderr, "An error occurred while advancing journal file: %s\n", strerror(rc));
telem_log(LOG_ERR, "An error occurred while advancing journal file: %s\n", strerror(rc));
}
}
while (getline(&line, &len, journal_fileptr) != -1) {
if (!strlen(line))
continue;
deserialize_journal_entry(line, &entry);
if (entry) {
/* filter entry out if one is provided */
@@ -498,16 +487,16 @@ int print_journal(TelemJournal *telem_journal, char *classification,
// Check prefixes when classification ends in /*, otherwise use strcomp
if (is_class_prefix(classification)) {
if (strncmp(entry->classification, classification, strlen(classification) - 1) != 0) {
continue;
goto skip_print;
}
} else if (strcmp(entry->classification, classification) != 0) {
continue;
goto skip_print;
}
}
/* 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;
goto skip_print;
}
/* print record metadata */
fprintf(stdout, "%-30s %s %s %s %s\n", entry->classification, str_time, entry->record_id, entry->event_id, entry->boot_id);
@@ -516,9 +505,9 @@ int print_journal(TelemJournal *telem_journal, char *classification,
print_record(entry->record_id);
}
count++;
}
skip_print:
free_journal_entry(entry);
free_journal_entry(entry);
}
}
free(line);
fclose(journal_fileptr);
@@ -563,9 +552,7 @@ int new_journal_entry(TelemJournal *telem_journal, char *classification,
struct JournalEntry *entry = NULL;
if (telem_journal == NULL) {
#ifdef DEBUG
fprintf(stderr, "Error: telem_journal was not initialized\n");
#endif
telem_log(LOG_ERR, "telem_journal was not initialized\n");
return rc;
}
@@ -579,7 +566,7 @@ int new_journal_entry(TelemJournal *telem_journal, char *classification,
entry = malloc(sizeof(struct JournalEntry));
if (!entry) {
fprintf(stderr, "Error: unable to allocate more memory\n");
telem_log(LOG_CRIT, "CRIT: unable to allocate memory\n");
return rc;
}
entry->classification = NULL;
@@ -588,16 +575,12 @@ int new_journal_entry(TelemJournal *telem_journal, char *classification,
entry->boot_id = NULL;
if (get_random_id(&record_id) != 0) {
#ifdef DEBUG
fprintf(stderr, "Error: unable to generate random id\n");
#endif
telem_log(LOG_ERR, "Erorr: Unable to generate random id\n");
goto quit;
}
if (read_boot_id(boot_id) != 0) {
#ifdef DEBUG
fprintf(stderr, "Error: unable to read boot_id\n");
#endif
telem_log(LOG_ERR, "Error: Unable to read boot_id\n");
goto quit;
}
@@ -615,9 +598,7 @@ int new_journal_entry(TelemJournal *telem_journal, char *classification,
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
telem_debug("DEBUG: %d records in journal\n", telem_journal->record_count);
}
free(telem_journal->latest_record_id);
@@ -647,9 +628,7 @@ int prune_journal(struct TelemJournal *telem_journal, char *tmp_dir)
// jump to line# count
if ((rc = skip_n_lines(count, telem_journal->fptr, telem_journal->prune_entry_callback)) != 0) {
#ifdef DEBUG
fprintf(stderr, "Error: skipping %d journal lines\n", count);
#endif
telem_log(LOG_ERR, "Error skipping %d journal lines\n", count);
// if rc == -1 (n > #lines in file, change to errno style)
return (rc == -1) ? EADDRNOTAVAIL : rc;
}
@@ -665,38 +644,28 @@ int prune_journal(struct TelemJournal *telem_journal, char *tmp_dir)
}
// 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
telem_log(LOG_ERR, "Error copying partial journal to temp journal file\n");
goto quit;
}
// close file handler
if ((rc = fclose(telem_journal->fptr)) != 0) {
#ifdef DEBUG
fprintf(stderr, "Error: closing journal file handler\n");
#endif
telem_log(LOG_ERR, "Error closing journal file handler\n");
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
telem_log(LOG_ERR, "Error while overwriting journal file\n");
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
telem_log(LOG_ERR, "Error re-opening journal file\n");
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
telem_debug("DEBUG: record_count: %d\n", telem_journal->record_count);
}
rc = 0;
+6
View File
@@ -5,5 +5,11 @@ bin_PROGRAMS += \
%D%/journal.c \
src/util.c \
src/common.c
%C%_telem_journal_CFLAGS = \
$(AM_CFLAGS)
if LOG_SYSTEMD
%C%_telem_journal_CFLAGS += $(SYSTEMD_JOURNAL_CFLAGS)
%C%_telem_journal_LDADD = $(SYSTEMD_JOURNAL_LIBS)
endif
# vim: filetype=automake tabstop=8 shiftwidth=8 noexpandtab
+15 -6
View File
@@ -1,12 +1,22 @@
bin_PROGRAMS = \
%D%/telemprobd \
%D%/telempostd
%D%/telempostd \
%D%/telemctl
%C%_telemctl_SOURCES = \
%D%/telemctl.c
%C%_telemctl_CFLAGS = \
$(AM_CFLAGS)
%C%_telemctl_LDFLAGS = \
$(AM_LDFLAGS) \
-pie
%C%_telemprobd_SOURCES = \
%D%/probe.c \
%D%/telemdaemon.c \
%D%/telemdaemon.h \
%D%/iorecord.c \
%D%/journal/journal.c \
%D%/journal/journal.h
@@ -51,7 +61,7 @@ endif
%D%/iorecord.c \
%D%/iorecord.h
%C%_telempostd_LDADD = $(CURL_LIBS) \
%C%_telempostd_LDADD = $(CURL_LIBS) $(JSON_C_LIBS) \
%D%/libtelem-shared.la \
%D%/libtelemetry.la
@@ -80,8 +90,8 @@ endif
# set library version info
SHAREDLIB_CURRENT=4
SHAREDLIB_REVISION=0
SHAREDLIB_AGE=1
SHAREDLIB_REVISION=1
SHAREDLIB_AGE=0
noinst_LTLIBRARIES = %D%/libtelem-shared.la
@@ -91,7 +101,6 @@ noinst_LTLIBRARIES = %D%/libtelem-shared.la
%D%/configuration.c \
%D%/nica/inifile.c \
%D%/nica/hashmap.c \
%D%/nica/b64enc.c \
%D%/configuration.h \
%D%/common.c \
%D%/common.h
+4 -4
View File
@@ -1,7 +1,7 @@
/*
* This program is part of the Clear Linux Project
*
* Copyright 2015 Intel Corporation
* Copyright 2019 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
@@ -17,6 +17,7 @@
#pragma once
#include <stdio.h>
#include <errno.h>
#include "config.h"
@@ -35,15 +36,14 @@
#include <systemd/sd-journal.h>
#endif
#else
#include <errno.h>
#include <string.h>
#include <syslog.h>
#endif
#ifdef DEBUG
#define telem_debug(...) do { \
(telem_log(LOG_DEBUG, "%s():[%d]", __func__, __LINE__), \
telem_log(LOG_DEBUG, __VA_ARGS__)); \
telem_log(LOG_DEBUG, "%s():[%d] ", __func__, __LINE__); \
telem_log(LOG_DEBUG, __VA_ARGS__); \
} while (0);
#else
#define telem_debug(...) do {} while (0);
-154
View File
@@ -1,154 +0,0 @@
/*
* This file is part of libnica.
*
* Copyright © 2017 Intel Corporation
*
* libnica is free software; you can redistribute it and/or modify
* it under the terms 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.
*/
#define _GNU_SOURCE
#include <stdio.h>
#include <stdint.h>
#include "b64enc.h"
#define B64_LINE_LEN 76
static int padding[] = {0, 2, 1, 0};
static char table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
/*
* base 64 encoding of 3 bytes to 4 printable characters
* (https://tools.ietf.org/html/rfc4648).
*
* i.e. (Reference: https://en.wikipedia.org/wiki/Base64)
*
* [ M ] [ a ] [ n ]
* bin 01001101 01100001 01101110
*
* b64 01001101 01100001 01101110 >> 18 & 0x3F -> 010011 -> ( 19 dec) T
* ------
*
* b64 01001101 01100001 01101110 >> 12 & 0x3F -> 010110 -> ( 22 dec) W
* -------
*
* b64 01001101 01100001 01101110 >> 6 & 0x3F -> 000101 -> ( 5 dec) F
* -------
*
* b64 01001101 01100001 01101110 >> 0 & 0x3F -> 101110 -> (117 dec) u
* ------
*
*/
static void b64_3b(char bin[3], char *out) {
uint32_t x = (((uint32_t) bin[0] << 16) & 0xFF0000) |
(((uint32_t) bin[1] << 8) & 0xFF00) |
(((uint32_t) bin[2]) & 0xFF);
out[3] = table[x & 0x3F];
out[2] = table[x >> 6 & 0x3F];
out[1] = table[x >> 12 & 0x3F];
out[0] = table[x >> 18 & 0x3F];
}
/*
* Provide padding to already initialized b64 output
*/
static void b64_padding(char *out, int pad_size) {
if (pad_size > 0) {
*out++ = '=';
}
if (pad_size > 1) {
*out++ = '=';
}
*out = '\0';
}
/*
* Encode file contents as b64 string
*/
int nc_b64enc_file(FILE *fh, char *buff, size_t buff_size) {
int ret_val = 1;
int out_count = 0;
int pad_size = 0;
char *buff_ptr = buff;
char bin[3] = {0, 0, 0};
size_t len = 0;
while ((len = fread(bin, 1, 3, fh)) > 0) {
b64_3b(bin, buff_ptr);
/*
Reset 2nd and 3rd bytes in case only one byte was read. If these
bytes are not clean b64 output will be corrupted with old bits.
*/
bin[1] = bin[2] = 0;
/*
Advance pointer to the next free position of the b64 encoded
buffer. This happens to be len + 1 (as longs as len >= 1 and len <= 3)
len = 1 byte -> b64 -> 2 bytes (or len + 1)
len = 2 bytes -> b64 -> 3 bytes (or len + 1)
len = 3 bytes -> b64 -> 4 bytes (or len + 1)
*/
buff_ptr += (len + 1);
/*
Line break every 76 characters
*/
out_count += (int) (len + 1);
if (out_count % B64_LINE_LEN == 0) {
*buff_ptr++ = '\n';
}
pad_size = padding[len];
/*
Check not to overflow buffer
buffer size <= b64 characters + padding length + null termination
*/
if (buff_size <= (size_t) (buff_ptr - buff) + (size_t) pad_size + 1) {
ret_val = 0;
goto end_b64_enc;
}
}
b64_padding(buff_ptr, pad_size);
end_b64_enc:
return ret_val;
}
int nc_b64enc_filename(const char *filename, char *buff, size_t buff_size) {
int ret = 0;
FILE *fh = NULL;
fh = fopen(filename, "rb");
if (fh == NULL) {
goto nc_b64_clean;
}
ret = nc_b64enc_file(fh, buff, buff_size);
nc_b64_clean:
if (fh) {
fclose(fh);
}
return ret;
}
/* vi: set ts=8 sw=8 sts=4 et tw=80 cino=(0: */
-42
View File
@@ -1,42 +0,0 @@
/*
* This file is part of libnica.
*
* Copyright © 2017 Intel Corporation
*
* libnica is free software; you can redistribute it and/or modify
* it under the terms 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 is a chained base64 encode implementation.
*/
#pragma once
#define _GNU_SOURCE
#include "macros.h"
/*
* Encodes contents of file handler in base64
*
* @param fh File handler to encode as base64
* @param buff Output buffer where the base64 output should be written
* @param buff_size Size of the output buffer
*
* @returns 1 in success and 0 in failure
*/
_nica_public_ int nc_b64enc_file(FILE *fh, char *buff, size_t buff_size);
/*
* Encodes contents of file (filename) in base64
*
* @param filename File to encode as base64
* @param buff Output buffer where the base64 output should be written
* @param buff_size Size of the output buffer
*
* @returns 1 in success and 0 in failure
*/
_nica_public_ int nc_b64enc_filename(const char *filename, char *buff, size_t buff_size);
+36 -24
View File
@@ -151,33 +151,38 @@ static char *string_strip(char *str, ssize_t len, ssize_t *out_len)
/* eat \s */
static char *string_unescape(char *str)
{
char *c, *c1, *c2, *c3;;
char *c, *c1, *c2, *c3;;
if (!str)
return NULL;
if (!str) {
return NULL;
}
c = calloc(strlen(str) + 1, 1);
c1 = c;
c = calloc(strlen(str) + 1, 1);
if (c == NULL) {
return NULL;
}
c2 = str;
while (c2 && *c2 != 0) {
c3 = c2 + 1;
if (*c2 == '\\') {
if (*c3 == 's') {
*c = ' ';
c++;
c2 += 2;
} else {
c2++;
}
} else {
*c = *c2;
c++;
c2++;
}
}
c1 = c;
free(str);
c2 = str;
while (c2 && *c2 != 0) {
c3 = c2 + 1;
if (*c2 == '\\') {
if (*c3 == 's') {
*c = ' ';
c++;
c2 += 2;
} else {
c2++;
}
} else {
*c = *c2;
c++;
c2++;
}
}
free(str);
return c1;
}
@@ -300,7 +305,14 @@ int nc_ini_file_parse_full(const char *path, NcHashmap **out_map, int *error_lin
key = strdup(buf);
key = string_chew_terminated(key);
value = string_chew_terminated(value);
value = string_unescape(value);
if (value != NULL) {
value = string_unescape(value);
if (value == NULL) {
err_ret = NC_INI_ERROR_INTERNAL;
fprintf(stderr, "[inifile] Fatal! Out of memory\n");
goto fail;
}
}
if (streq(key, "")) {
err_ret = NC_INI_ERROR_EMPTY_KEY;
+8 -2
View File
@@ -27,7 +27,13 @@
#include "configuration.h"
#include "telempostdaemon.h"
bool (*post_record_ptr)(char *[], char *) = post_record_http;
/*
* Using a function pointer for unit testing to isolate the call to actual post function.
* The call to post the record to the server is stubbed out in the unit tests
* using pointer to a fake function.
*/
bool (*post_record_ptr)(char *[], char *, char *) = post_record_http;
void print_usage(char *prog)
{
@@ -72,7 +78,7 @@ int main(int argc, char **argv)
}
}
initialize_daemon(&daemon);
initialize_post_daemon(&daemon);
daemon.current_spool_size = get_spool_dir_size();
+3 -9
View File
@@ -51,12 +51,6 @@
#include "telemdaemon.h"
#include "configuration.h"
/*
* Using a function pointer for unit testing to isolate the call to actual post function.
* The call to post the record to the server is stubbed out in the unit tests
* using pointer to a fake function.
*/
void print_usage(char *prog)
{
printf("%s: Usage\n", prog);
@@ -111,7 +105,7 @@ int main(int argc, char **argv)
exit(EXIT_FAILURE);
}
}
initialize_daemon(&daemon);
initialize_probe_daemon(&daemon);
sigemptyset(&mask);
@@ -249,8 +243,8 @@ int main(int argc, char **argv)
}
if (fdsi.ssi_signo == SIGTERM || fdsi.ssi_signo == SIGINT) {
telem_log(LOG_INFO, "Received either a \
SIGINT/SIGTERM signal\n");
telem_log(LOG_INFO, "Received either a "
"SIGINT/SIGTERM signal\n");
goto clean_exit;
}
+1 -6
View File
@@ -2,9 +2,6 @@
The default probes provided along the telemetry client code are:
* bertprobe: this probe reports on the Boot Error Region Table if such
entry is found in ```/sys/firmware/acpi/tables/BERT```.
* crashprobe: This probe processes core dump files. It can be registered as
the kernel core file handler in /proc/sys/kernel/core_pattern.
@@ -15,12 +12,10 @@ The default probes provided along the telemetry client code are:
from failed services.
* klogscanner: a probe to collect 'oops messages' when the kernel detects a
problem.
problem. Also reports errors in the Boot Error Region Table if detected.
* pstoreprobe: probe to collect messages left on pstore filesystem.
* pythonprobe: a probe that monitors Python generated traceback files.
* telem-record-gen: this is a "general-purpose probe" for sending custom
records on-the-fly. This tool can be used stand alone or as a part of a
script to implement a probe.
-117
View File
@@ -1,117 +0,0 @@
/*
* 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 <sys/stat.h>
#include <getopt.h>
#include "common.h"
#include "telemetry.h"
#include "nica/b64enc.h"
#include "config.h"
#include "log.h"
static const char bert_record_file[] = "/sys/firmware/acpi/tables/data/BERT";
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);
printf(" -f, --config_file Specify a configuration file other than default\n");
printf(" -h, --help Display this help message\n");
printf(" -V, --version Print the program version\n");
}
int main(int argc, char **argv)
{
struct telem_ref *tm_handle = NULL;
char *classification = (char *)telem_record_class;
char *payload;
int ret;
// Following vars are for arg parsing.
int c;
int opt_index = 0;
struct option opts[] = {
{ "config_file", 1, NULL, 'f' },
{ "help", 0, NULL, 'h' },
{ "version", 0, NULL, 'V' },
{ NULL, 0, NULL, 0 }
};
while ((c = getopt_long(argc, argv, "f:hV", opts, &opt_index)) != -1) {
switch (c) {
case 'f':
if (tm_set_config_file(optarg) != 0) {
telem_log(LOG_ERR, "Configuration file"
" path not valid\n");
exit(EXIT_FAILURE);
}
break;
case 'h':
print_usage(argv[0]);
exit(EXIT_SUCCESS);
case 'V':
printf(PACKAGE_VERSION "\n");
exit(EXIT_SUCCESS);
case '?':
exit(EXIT_FAILURE);
}
}
payload = calloc(sizeof(char), MAX_PAYLOAD_LENGTH);
if (!payload) {
printf("Unable to allocate more memory.\n");
return -ENOMEM;
}
if (!(ret = nc_b64enc_filename(bert_record_file, payload, MAX_PAYLOAD_LENGTH))) {
printf("Failed to read payload from: %s\n", bert_record_file);
ret = EXIT_FAILURE;
goto done;
}
if ((ret = tm_create_record(&tm_handle, severity, classification,
payload_version)) < 0) {
printf("Failed to create record: %s\n", strerror(-ret));
goto done;
}
if ((ret = tm_set_payload(tm_handle, payload)) < 0) {
printf("Failed to set record payload: %s\n", strerror(-ret));
goto done;
}
if ((ret = tm_send_record(tm_handle)) < 0) {
printf("Failed to send record to daemon: %s\n", strerror(-ret));
goto done;
}
ret = EXIT_SUCCESS;
done:
free(payload);
tm_free_record(tm_handle);
tm_handle = NULL;
return ret;
}
/* vi: set ts=8 sw=8 sts=4 et tw=80 cino=(0: */
+21 -1
View File
@@ -41,6 +41,8 @@
#include "probe.h"
#include "telemetry.h"
#define FRAMES_MAX 64
static Dwfl *d_core = NULL;
/*
@@ -248,7 +250,7 @@ static int frame_cb(Dwfl_Frame *frame, void *userdata)
" current frame: %s\n",
dwfl_errmsg(-1));
if (ret < 0) {
return DWARF_CB_ABORT;
errorstr = NULL;
}
return DWARF_CB_ABORT;
}
@@ -298,6 +300,12 @@ static int frame_cb(Dwfl_Frame *frame, void *userdata)
}
}
nc_string_append_printf(*bt, "\n");
if (frame_counter >= FRAMES_MAX) {
errorstr = NULL;
return DWARF_CB_ABORT;
}
return DWARF_CB_OK;
}
@@ -388,8 +396,15 @@ static int process_corefile(nc_string **backtrace)
nc_string_free(*backtrace);
}
*backtrace = nc_string_dup("");
frame_counter = 0;
if (dwfl_getthreads(d_core, thread_cb, backtrace) != DWARF_CB_OK) {
/* We aborted unwinding, due to too many frames.
* We don't consider this as an error.
*/
if (frame_counter >= FRAMES_MAX) {
return 0;
}
/* When errors occur during the unwinding, we reach this point.
* If an error string is set for the particular error, send an
* "error" record to capture at least a partial backtrace if
@@ -656,6 +671,11 @@ int main(int argc, char **argv)
}
}
if (frame_counter >= FRAMES_MAX) {
telem_log(LOG_ERR, "Too many frames. Backtrace truncated.\n");
nc_string_append_printf(header, "Too many frames. Backtrace truncated.\n");
}
nc_string_prepend(backtrace, header->str);
if (!send_data(&backtrace, default_severity, clr_class)) {
+1 -1
View File
@@ -90,7 +90,7 @@ static char *create_payload(unsigned options)
num_bundles = scandir("/usr/share/clear/bundles", &entries, nodots, alphasort);
if (num_bundles < 0) {
telem_log(LOG_ERR, "scandir failed: %s\n", strerror(errno));
telem_perror("scandir failed");
free(payload);
return NULL;
}
+2 -2
View File
@@ -51,7 +51,7 @@ int main(void)
// Gets the size of the kernel ring buffer
log_size = klogctl(SYSLOG_ACTION_SIZE_BUFFER, NULL, 0);
if (log_size < 0) {
telem_log(LOG_ERR, "Cannot read size of kernel ring buffer: %s\n", strerror(errno));
telem_perror("Cannot read size of kernel ring buffer");
return 1;
}
@@ -70,7 +70,7 @@ int main(void)
memset(bufp, 0, buflen);
bytes_read = klogctl(SYSLOG_ACTION_READ, bufp, (int)buflen);
if (bytes_read < 0) {
telem_log(LOG_ERR, "Cannot read contents of kernel ring buffer: %s\n", strerror(errno));
telem_perror("Cannot read contents of kernel ring buffer");
return 1;
}
+15 -12
View File
@@ -23,6 +23,7 @@
#include <unistd.h>
#include <sys/stat.h>
#include "common.h"
#include "log.h"
#include "oops_parser.h"
#include "klog_scanner.h"
@@ -44,6 +45,11 @@ static bool send_data(char *backtrace, char *class, uint32_t severity)
return false;
}
/* Truncate payload if necessary, otherwise nothing will be sent */
if (strlen(backtrace) > MAX_PAYLOAD_LENGTH) {
backtrace[MAX_PAYLOAD_LENGTH-1] = 0;
}
if ((ret = tm_set_payload(handle, backtrace)) < 0) {
telem_log(LOG_ERR, "Failed to set payload: %s", strerror(-ret));
tm_free_record(handle);
@@ -91,10 +97,8 @@ void split_buf_by_line(char *bufp, int bytes)
start[linelength] = '\0';
parse_single_line(start, linelength);
if (oops_processed == true) {
#ifdef DEBUG
printf("[%s] oops_processed detected !\n", __func__);
#endif
break;
telem_debug("oops_processed detected !\n");
break;
}
linelength = 0;
}
@@ -128,7 +132,8 @@ void klog_process_oops_msgs(struct oops_log_msg *msg)
//Add the newline character to the end of each line
line = msg->lines[i];
linelength = strlen(line);
strncpy(bp, line, linelength);
// Copy the line (without the terminating NULL)
memcpy(bp, line, linelength);
bp[linelength] = '\n';
done += linelength + 1;
bp = contents + done;
@@ -136,16 +141,14 @@ void klog_process_oops_msgs(struct oops_log_msg *msg)
struct oops_log_msg oops_msg;
if (handle_entire_oops(contents, (long)size, &oops_msg)) {
#ifdef DEBUG
printf("Raw message:\n");
#ifdef DEBUG
telem_debug("DEBUG: Raw oops message:\n");
for (int i = 0; i < oops_msg.length; i++) {
printf("%s\n", oops_msg.lines[i]);
telem_log(LOG_DEBUG, "%s\n", oops_msg.lines[i]);
}
#endif
#endif
payload = parse_payload(&oops_msg);
#ifdef DEBUG
printf("Payload Parsed :%s\n", payload->str);
#endif
telem_debug("DEBUG: Payload Parsed :%s\n", payload->str);
oops_msg_cleanup(&oops_msg);
send_data(payload->str, (char *)oops_msg.pattern->classification, (uint32_t)oops_msg.pattern->severity);
nc_string_free(payload);
+20 -22
View File
@@ -4,28 +4,21 @@ bin_PROGRAMS += \
%D%/telem-record-gen \
%D%/klogscanner \
%D%/pstoreprobe \
%D%/pythonprobe \
%D%/pstoreclean \
%D%/bertprobe
%C%_pythonprobe_SOURCES = %D%/python-probe.c
%C%_pythonprobe_LDADD = $(top_builddir)/src/libtelemetry.la
%C%_pythonprobe_LDFLAGS = \
$(AM_LDFLAGS) \
-pie
%D%/pstoreclean
%C%_hprobe_SOURCES = %D%/hello.c
%C%_hprobe_LDADD = $(top_builddir)/src/libtelemetry.la
%C%_hprobe_CFLAGS = $(AM_CFLAGS)
%C%_hprobe_LDFLAGS = \
$(AM_LDFLAGS) \
-pie
%C%_bertprobe_SOURCES = %D%/bert_probe.c \
src/nica/b64enc.c
%C%_bertprobe_LDADD = $(top_builddir)/src/libtelemetry.la
%C%_bertprobe_LDFLAGS = \
$(AM_LDFLAGS) \
-pie
if LOG_SYSTEMD
if HAVE_SYSTEMD_JOURNAL
%C%_hprobe_CFLAGS += $(SYSTEMD_JOURNAL_CFLAGS)
%C%_hprobe_LDADD += $(SYSTEMD_JOURNAL_LIBS)
endif
endif
%C%_telem_record_gen_SOURCES = %D%/telem_record_gen.c
%C%_telem_record_gen_CFLAGS = \
@@ -35,6 +28,14 @@ bin_PROGRAMS += \
$(AM_LDFLAGS) \
-pie
if LOG_SYSTEMD
if HAVE_SYSTEMD_JOURNAL
%C%_telem_record_gen_CFLAGS += $(SYSTEMD_JOURNAL_CFLAGS)
%C%_telem_record_gen_LDADD += $(SYSTEMD_JOURNAL_LIBS)
endif
endif
%C%_pstoreclean_SOURCES = %D%/pstore_clean.c
%C%_pstoreclean_CFLAGS = \
$(AM_CFLAGS)
@@ -113,13 +114,6 @@ endif
-pie
if LOG_SYSTEMD
if HAVE_SYSTEMD_JOURNAL
%C%_klogscanner_CFLAGS += \
$(SYSTEMD_JOURNAL_CFLAGS)
%C%_klogscanner_LDADD += \
$(SYSTEMD_JOURNAL_LIBS)
endif
if HAVE_SYSTEMD_JOURNAL
%C%_klogscanner_CFLAGS += \
$(SYSTEMD_JOURNAL_CFLAGS)
@@ -149,6 +143,10 @@ bin_PROGRAMS += \
%C%_journalprobe_LDFLAGS = \
$(AM_LDFLAGS) \
-pie
if LOG_SYSTEMD
%C%_journalprobe_CFLAGS += $(SYSTEMD_JOURNAL_CFLAGS)
%C%_journalprobe_LDADD += $(SYSTEMD_JOURNAL_LIBS)
endif
endif
endif
+78 -14
View File
@@ -150,6 +150,24 @@ struct oops_pattern oops_patterns_arr[] = {
TM_MEDIUM,
false,
},
{
"ACPI Error:",
"org.clearlinux/kernel/warning",
TM_MEDIUM,
false,
},
{
"Kernel panic - not syncing:",
"org.clearlinux/kernel/panic",
TM_CRITICAL,
false,
},
{
"BERT: Error records from previous boot:",
"org.clearlinux/bert/debug",
TM_MEDIUM,
false,
},
};
static int oops_patterns_cnt = sizeof(oops_patterns_arr) / sizeof(struct oops_pattern);
@@ -322,6 +340,7 @@ void oops_msg_cleanup(struct oops_log_msg *msg)
static struct oops_log_msg oops_msg;
static bool in_stack_dump = false;
static bool in_bert_dump = false;
static oops_handler_t oops_handler;
@@ -345,6 +364,19 @@ void oops_parser_cleanup()
free_pattern_regex();
}
/*
*
[ 1.609112] BERT: Error records from previous boot:
[ 1.609113] [Hardware Error]: event severity: fatal
[ 1.609115] [Hardware Error]: Error 0, type: fatal
[ 1.609116] [Hardware Error]: section type: unknown, 81212a96-09ed-4996-9471-8d729c8e69ed
[ 1.609117] [Hardware Error]: section length: 0xc10
[ 1.609119] [Hardware Error]: 00000000: 00000001 00000000 00000000 01001002 ................
[ 1.609120] [Hardware Error]: 00000010: 01001002 00000001 c71f0d2f 00000003 ......../.......
[ 1.609122] [Hardware Error]: 00000020: 00000001 0000004f 0c070048 800001ff ....O...H.......
[ 1.609123] [Hardware Error]: 00000030: 02002080 7e15fe03 00000001 00000000 . .....~......
...
*/
void parse_single_line(char *line, size_t size)
{
char *start;
@@ -364,28 +396,30 @@ void parse_single_line(char *line, size_t size)
continue;
}
telem_log(LOG_DEBUG, "Oops start has been detected\n");
oops_msg.pattern = pattern;
oops_msg.lines[oops_msg.length] = strndup(start, (size_t)(line_end - start));
if (oops_msg.lines[oops_msg.length] == NULL) {
//telem_perror("Failed to copy string");
exit(EXIT_FAILURE);
return;
} else {
oops_msg.length++;
}
oops_msg.length++;
in_stack_dump = false;
break;
in_bert_dump = false;
if (strstr(oops_msg.pattern->begin_line, "BERT:")) {
in_bert_dump = true;
}
return;
}
} else {
// If in the middle of oops
if (oops_msg.length >= MAX_LINES) {
fprintf(stderr,"*** MAX_LINES!!\n");
end_found = true;
// } else if (oops_msg.end_line && strstr(start, oops_msg.end_line)) {
// end_found = false;
} else if (strstr(start, "[ end trace")) {
end_found = true;
} else if (!in_stack_dump) {
} else if (!in_stack_dump && !in_bert_dump) {
// This line indicates the beginning of a stack trace;
// the next line is most likely the topmost frame.
if (starts_with(start, line_end, "Call Trace:")) {
@@ -402,6 +436,10 @@ void parse_single_line(char *line, size_t size)
in_stack_dump = false;
end_found = true;
}
} else if (in_bert_dump) {
if (!strstr(start, "[Hardware Error]:")) {
end_found = true;
}
}
/* if a new oops starts, this one has ended */
@@ -469,7 +507,7 @@ static void stack_frame_append(struct stack_frame **head, struct stack_frame **t
while (*start && !isspace(*start)) {
start++;
}
if (start && *start == '\0') {
if (*start == '\0') {
return;
}
@@ -504,7 +542,7 @@ static void stack_frame_append(struct stack_frame **head, struct stack_frame **t
while (*start && !isspace(*start)) {
start++;
}
if (start && *start == '\0') {
if (*start == '\0') {
return;
}
@@ -539,6 +577,10 @@ static void stack_frame_free(struct stack_frame **head)
while (*head != NULL) {
frame = (*head)->next;
// First free memory allocation for function name
if ((*head)->function) {
free((*head)->function);
}
free(*head);
*head = frame;
}
@@ -546,8 +588,8 @@ static void stack_frame_free(struct stack_frame **head)
/*
* Function parses lines of the format :
* CPU: 2 PID: 6429 Comm: insmod Tainted: P OE 3.19.0-18-generic #18-Ubuntu$
* CPU: 2 PID: 0 Comm: swapper/2 Not tainted 3.10.4-100.fc18.x86_64 #1
* CPU: 2 PID: 6429 Comm: insmod Tainted: POE 3.19.0-18-generic #18-Ubuntu$
* CPU: 2 PID: 0 Comm: swapper/2 Not tainted 3.10.4-100.fc18.x86_64 #1
* CPU: 3 PID: 0 Comm: swapper/3 Not tainted 4.0.5-300.fc22.x86_64 #1
*/
static void parse_kernel_cpu_line(char *line, char **kernel_version, char **tainted)
@@ -796,6 +838,15 @@ static nc_string *parse_backtrace(struct oops_log_msg *msg)
if (str_starts_with_casei(line, "CPU: ") ||
str_starts_with_casei(line, "PID: ")) {
/* Can't assume that these variables (tainted,
* an kernel_version) are not already allocated
* otherwise it will leak memory */
if (tainted) {
free(tainted);
}
if (kernel_version) {
free(kernel_version);
}
parse_kernel_cpu_line(line, &kernel_version, &tainted);
continue;
}
@@ -835,15 +886,28 @@ static nc_string *parse_backtrace(struct oops_log_msg *msg)
return backtrace;
}
static void append_payload(nc_string *payload, struct oops_log_msg *msg)
{
for (int i = 1; i < msg->length; i++) {
nc_string_cat(payload,msg->lines[i]);
nc_string_cat(payload,"\n");
}
}
nc_string *parse_payload(struct oops_log_msg *msg)
{
nc_string *payload, *backtrace;
payload = nc_string_dup("Crash Report:\n");
nc_string_append_printf(payload, "Reason: %s\n", msg->lines[0]);
backtrace = parse_backtrace(msg);
nc_string_cat(payload, backtrace->str);
nc_string_free(backtrace);
if (strstr(msg->lines[0], "BERT:")) {
append_payload(payload, msg);
} else {
backtrace = parse_backtrace(msg);
nc_string_cat(payload, backtrace->str);
nc_string_free(backtrace);
}
return payload;
}
+1 -1
View File
@@ -132,7 +132,7 @@ int main(int argc, char **argv)
if (mkdir(MOUNT, 0755) < 0) {
//If there is an error other than an existing directory
if (errno != EEXIST) {
telem_log(LOG_ERR, "Error creating directory /dev/pstore : %s\n", strerror(errno));
telem_perror("Error creating directory /dev/pstore");
//bail out?
exit(EXIT_FAILURE);
}
+44 -10
View File
@@ -15,6 +15,7 @@
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <dirent.h>
#include <string.h>
#include <errno.h>
@@ -65,14 +66,12 @@ void handle_complete_oops_message(struct oops_log_msg *msg)
#ifdef DEBUG
for (int i = 0; i < msg->length; i++) {
printf("%s\n", msg->lines[i]);
telem_debug("DEBUG: %s\n", msg->lines[i]);
}
#endif
payload = parse_payload(msg);
#ifdef DEBUG
printf("Payload Parsed :%s\n", payload->str);
#endif
telem_debug("DEBUG: Payload Parsed :%s\n", payload->str);
send_data(payload->str, (char *)msg->pattern->classification, (uint32_t)msg->pattern->severity);
nc_string_free(payload);
}
@@ -197,6 +196,13 @@ struct chunk_list {
struct chunk_list *next;
};
static void print_usage(char *prog)
{
printf("%s: Usage\n", prog);
printf(" -f, --config_file This overides the other parameters\n");
printf(" -h, --help Display this help message\n");
}
int main(int argc, char **argv)
{
DIR *pstore_dir;
@@ -207,6 +213,32 @@ int main(int argc, char **argv)
int max_part;
size_t totalsize = 0;
char *crash_dump = NULL;
int c;
int opt_index = 0;
struct option opts[] = {
{ "config_file", 1, NULL, 'f' },
{ "help", 0, NULL, 'h' },
{ NULL, 0, NULL, 0 }
};
while ((c = getopt_long(argc, argv, "f:h", opts, &opt_index)) != -1) {
switch (c) {
case 'f':
if (tm_set_config_file(optarg) != 0) {
telem_log(LOG_ERR, "Configuration file"
" path not valid\n");
exit(EXIT_FAILURE);
}
break;
case 'h':
print_usage(argv[0]);
exit(EXIT_SUCCESS);
case '?':
print_usage(argv[0]);
exit(EXIT_FAILURE);
}
}
/*
* Hash table used to store chunks belonging to a oops counter
@@ -232,9 +264,13 @@ int main(int argc, char **argv)
// Look for only dmesg logs. Ignore other types of pstore dumps for now.
if (sscanf(entry->d_name, "dmesg-efi-%" PRIu64, &id) == 1) {
parse_id(id, &count, &part);
#ifdef DEBUG
printf("Extracted count :%d, part : %d\n", count, part);
#endif
telem_debug("dmesg-efi Extracted count :%d, part : %d\n",
count, part);
} else if (sscanf(entry->d_name, "dmesg-ramoops-%" PRIu64, &id) == 1) {
count = (int)(id + 1);
part = 1;
telem_debug("dmesg-ramoops Extracted count :%d, part : %d\n",
count, part);
} else {
continue;
}
@@ -262,9 +298,7 @@ int main(int argc, char **argv)
nc_hashmap_iter_init(hash, &iter);
while (nc_hashmap_iter_next(&iter, (void **)&key, (void **)&value)) {
#ifdef DEBUG
printf("Count in the hash: %d\n", NC_UNHASH_KEY(key));
#endif
telem_debug("DEBUG: Count in the hash: %d\n", NC_UNHASH_KEY(key));
head = (struct chunk_list *)value;
elem = head;
-293
View File
@@ -1,293 +0,0 @@
/*
* This program is part of the Clear Linux Project
*
* Copyright 2019 Intel Corporation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#define _GNU_SOURCE
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/inotify.h>
#include <fcntl.h>
#include <dirent.h>
#include <pwd.h>
#include <grp.h>
#include <getopt.h>
#include "log.h"
#include "telemetry.h"
#define PYTHON_TELEMETRY_DIR "/var/lib/telemetry/python"
/* We expect something like: 1.python-exception.3.N2Qy24 */
static bool validate_file_name(const char *filename, uint32_t *severity,
char** class, uint32_t *version)
{
unsigned int ver, sev;
char cls[PATH_MAX] = { 0 };
char rnd[PATH_MAX] = { 0 };
char *filename_base = basename(filename);
char *str_class = NULL;
if (!filename_base) {
telem_perror("basename failed");
return false;
}
if (sscanf(filename_base,"%u.%[^]0-9.].%u.%s", &ver, cls, &sev, rnd) == 4) {
if (asprintf(&str_class, "org.clearlinux/python/%s", cls) < 29) {
telem_log(LOG_ERR, "%s: invalid telemetry classification\n",
str_class);
return false;
}
*class = str_class;
*version = ver;
*severity = sev;
return true;
}
return false;
}
static char *read_file_into_buffer(const char *filename)
{
FILE *fp = NULL;
char *contents = NULL;
long sz;
size_t size, bytes_read;
fp = fopen(filename, "r");
if (fp == NULL) {
telem_log(LOG_ERR, "Failed to open Python telemetry file %s: %s\n",
filename, strerror(errno));
return NULL;
}
if (fseek(fp, 0, SEEK_END) == -1) {
telem_log(LOG_ERR, "Failed to seek file %s: %s\n", filename,
strerror(errno));
goto error_end;
}
sz = ftell(fp);
if (sz == -1L) {
telem_perror("ftell failed");
goto error_end;
}
if (sz == 0L) {
telem_perror("File is empty\n");
goto error_end;
}
if (fseek(fp, 0, SEEK_SET) < 0) {
telem_log(LOG_ERR, "fseek failed for file %s:%s\n", filename,
strerror(errno));
goto error_end;
}
size = (size_t)sz;
contents = calloc(sizeof(char), size);
if (!contents) {
telem_log(LOG_ERR, "Call to calloc failed\n");
goto error_end;
}
bytes_read = fread(contents, sizeof(char), size, fp);
if (bytes_read < size) {
telem_log(LOG_ERR, "Error while reading file %s:%s\n", filename,
strerror(errno));
goto error_end;
}
fclose(fp);
return contents;
error_end:
fclose(fp);
if (contents) {
free(contents);
}
return NULL;
}
static void send_data(char *contents, uint32_t severity, char *class,
uint32_t version)
{
struct telem_ref *handle = NULL;
int ret;
if ((ret = tm_create_record(&handle, severity, class, version)) < 0) {
telem_log(LOG_ERR, "Failed to create record: %s\n",
strerror(-ret));
return;
}
if ((ret = tm_set_payload(handle, contents)) < 0) {
telem_log(LOG_ERR, "Failed to set payload: %s\n",
strerror(-ret));
tm_free_record(handle);
return;
}
if ((ret = tm_send_record(handle)) < 0) {
telem_log(LOG_ERR, "Failed to send record: %s\n",
strerror(-ret));
}
tm_free_record(handle);
}
/* Send a valid file to the backend. */
static void deliver_payload(const char *filename)
{
uint32_t severity;
uint32_t version;
char *class = NULL;
char *contents = NULL;
if (validate_file_name(filename, &severity, &class, &version)) {
if ((contents = read_file_into_buffer(filename)) != NULL) {
send_data(contents, severity, class, version);
free(contents);
}
free(class);
}
/* Keep PYTHON_TELEMETRY_DIR empty. Delete the file. */
if (unlink(filename) != 0) {
telem_log(LOG_ERR, "Failed to unlink %s: %s\n", filename,
strerror(errno));
}
}
static void drop_privs(void)
{
uid_t euid;
euid = geteuid();
if (euid != 0) {
telem_log(LOG_DEBUG, "Not root; skipping privilege drop\n");
return;
}
struct passwd *pw;
pw = getpwnam("telemetry");
if (!pw) {
telem_log(LOG_ERR, "telemetry user not found\n");
exit(EXIT_FAILURE);
}
// The order is important here:
// change supplemental groups, our gid, and then our uid
if (initgroups(pw->pw_name, pw->pw_gid) != 0) {
telem_perror("Failed to set supplemental group list");
exit(EXIT_FAILURE);
}
if (setgid(pw->pw_gid) != 0) {
telem_perror("Failed to set GID");
exit(EXIT_FAILURE);
}
if (setuid(pw->pw_uid) != 0) {
telem_perror("Failed to set UID");
exit(EXIT_FAILURE);
}
assert(getuid() == pw->pw_uid);
assert(geteuid() == pw->pw_uid);
assert(getgid() == pw->pw_gid);
assert(getegid() == pw->pw_gid);
}
static void print_usage(char *prog)
{
printf("%s: Usage\n", prog);
printf(" -f, --config_file Specify a configuration file other than default\n");
printf(" -h, --help Display this help message\n");
printf(" -V, --version Print the program version\n");
}
int main(int argc, char **argv)
{
DIR *dir;
struct dirent *entry;
int c;
int opt_index = 0;
struct option opts[] = {
{ "config_file", 1, NULL, 'f' },
{ "help", 0, NULL, 'h' },
{ "version", 0, NULL, 'V' },
{ NULL, 0, NULL, 0 }
};
drop_privs();
while ((c = getopt_long(argc, argv, "f:hV", opts, &opt_index)) != -1) {
switch (c) {
case 'f':
if (tm_set_config_file(optarg) != 0) {
telem_log(LOG_ERR, "Configuration file"
" path not valid\n");
exit(EXIT_FAILURE);
}
break;
case 'h':
print_usage(argv[0]);
exit(EXIT_SUCCESS);
case 'V':
printf(PACKAGE_VERSION "\n");
exit(EXIT_SUCCESS);
case '?':
exit(EXIT_FAILURE);
}
}
if (chdir(PYTHON_TELEMETRY_DIR)) {
perror(PYTHON_TELEMETRY_DIR);
exit(EXIT_FAILURE);
}
/* Process all existing files */
dir = opendir(PYTHON_TELEMETRY_DIR);
if (!dir) {
perror(PYTHON_TELEMETRY_DIR);
exit(EXIT_FAILURE);
}
while (true) {
entry = readdir(dir);
if (!entry) {
break;
}
if (entry->d_name[0] == '.') {
continue;
}
deliver_payload(entry->d_name);
}
closedir(dir);
exit(EXIT_SUCCESS);
}
+81 -73
View File
@@ -1,7 +1,7 @@
/*
* This program is part of the Clear Linux Project
*
* Copyright 2015 Intel Corporation
* Copyright 2019 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
@@ -32,7 +32,6 @@
#include "common.h"
#include "telemetry.h"
static char *config_file = NULL;
static uint32_t severity = 1;
static char *opt_class = NULL;
static char *opt_payload = NULL;
@@ -53,6 +52,7 @@ static const struct option prog_opts[] = {
{ "payload-file", required_argument, 0, 'P' },
{ "record-version", required_argument, 0, 'R' },
{ "event-id", required_argument, 0, 'e' },
{ "config-file", required_argument, 0, 'f' },
{ 0, 0, 0, 0 }
};
@@ -74,31 +74,18 @@ static void print_help(void)
printf(" -e, --event-id Event id to use in the record\n");
printf(" -o, --echo Echo record to stdout\n");
printf(" -n, --no-post Do not post record just print\n");
printf(" -f, --config_file Specify a configuration file other than default\n");
printf("\n");
}
const unsigned int count_chars(const char *check, const char character)
static bool parse_options(int argc, char **argv)
{
unsigned int count = 0U;
if (check == NULL) {
return count;
}
for (; *check != '\0'; ++check) {
if (*check == character) {
++count;
}
}
return count;
}
int parse_options(int argc, char **argv)
{
int ret = 0;
bool ret = false;
char *endptr = NULL;
long unsigned int tmp = 0;
int opt;
while ((opt = getopt_long(argc, argv, "hc:Vs:c:p:P:R:e:on", prog_opts, NULL)) != -1) {
while ((opt = getopt_long(argc, argv, "hc:Vs:c:p:P:R:e:onf:", prog_opts, NULL)) != -1) {
switch (opt) {
case 'h':
print_help();
@@ -123,12 +110,21 @@ int parse_options(int argc, char **argv)
severity = (uint32_t)tmp;
break;
case 'c':
if (opt_class != NULL) {
free(opt_class);
}
opt_class = strdup(optarg);
break;
case 'p':
if (opt_payload != NULL) {
free(opt_payload);
}
opt_payload = strdup(optarg);
break;
case 'P':
if (opt_payload_file != NULL) {
free(opt_payload_file);
}
opt_payload_file = strdup(optarg);
break;
case 'R':
@@ -148,6 +144,9 @@ int parse_options(int argc, char **argv)
payload_version = (uint32_t)tmp;
break;
case 'e':
if (opt_event_id != NULL) {
free(opt_event_id);
}
opt_event_id = strdup(optarg);
if (opt_event_id == NULL) {
goto fail;
@@ -159,31 +158,38 @@ int parse_options(int argc, char **argv)
case 'n':
opt_nopost = true;
break;
case 'f':
if (tm_set_config_file(optarg) != 0) {
telem_log(LOG_ERR, "Configuration file path not valid\n");
exit(EXIT_FAILURE);
}
break;
}
}
ret = 1;
ret = true;
fail:
return ret;
}
int validate_opts(void)
static bool validate_opts(void)
{
size_t len;
int ret = 0;
size_t i, len;
int x, slashes = 0;
const char alphab[] = EVENT_ID_ALPHAB;
bool ret = false;
/* classification */
if (opt_class == NULL) {
fprintf(stderr, "Error: Classification required. See --help.\n");
return ret;
}
len = strlen(opt_class);
if ((len == 0) || (len > 120)) {
if ((len == 0) || (len > MAX_CLASS_LENGTH)) {
fprintf(stderr, "Error: Valid size for classification "
"is 1-120 chars\n");
"is 1-%d chars\n", MAX_CLASS_LENGTH);
return ret;
}
@@ -195,12 +201,27 @@ int validate_opts(void)
}
}
if (count_chars(opt_class, '/') != 2) {
for (i = 0, x = 0; i <= (len - 1); i++, x++) {
if (opt_class[i] == '/') {
slashes++;
x = 0;
} else {
if (x > MAX_SUBCAT_LENGTH) {
fprintf(stderr, "Error: Classification strings"
" between slashes should have at most"
" %d chars\n", MAX_SUBCAT_LENGTH);
return ret;
}
}
}
if (slashes != 2) {
fprintf(stderr, "Error: Classification needs to be in "
"most/to/least specific format, 2 \'/\' required.\n");
return ret;
}
/* Severity */
if ((severity) < 1 || (severity > 4)) {
fprintf(stderr, "Error: Valid range for severity is 1-4\n");
@@ -221,29 +242,12 @@ int validate_opts(void)
}
}
return 1;
return true;
}
int allocate_payload_buffer(char **payload)
static bool get_payload_from_file(char **payload)
{
int ret = 0;
*payload = (char *)malloc(MAX_PAYLOAD_LENGTH);
if (*payload == NULL) {
goto out1;
}
*payload = memset(*payload, 0, MAX_PAYLOAD_LENGTH);
ret = 1;
out1:
return ret;
}
int get_payload_from_file(char **payload)
{
int ret = 1;
bool ret = false;
FILE *fp = NULL;
size_t bytes_in = 0;
@@ -260,10 +264,10 @@ int get_payload_from_file(char **payload)
/* if fread fails */
if (bytes_in == 0 && ferror(fp) != 0) {
ret = 0;
goto out;
}
ret = true;
out:
if (fp) {
fclose(fp);
@@ -272,7 +276,7 @@ out:
}
void get_payload_from_opt(char **payload)
static void get_payload_from_opt(char **payload)
{
size_t len = 0;
@@ -280,10 +284,12 @@ void get_payload_from_opt(char **payload)
if (len >= MAX_PAYLOAD_LENGTH) {
len = MAX_PAYLOAD_LENGTH - 1;
}
strncpy(*payload, opt_payload, len);
/* "payload" is pre-allocated zeroed buffer of MAX_PAYLOAD_LENGTH */
memcpy(*payload, opt_payload, len);
}
void get_payload_from_stdin(char **payload)
static void get_payload_from_stdin(char **payload)
{
size_t bytes_in = 0;
int c;
@@ -299,40 +305,40 @@ void get_payload_from_stdin(char **payload)
}
int get_payload(char **payload)
static bool get_payload(char **payload)
{
int ret = 0;
bool ret = false;
if (!allocate_payload_buffer(payload)) {
goto out1;
}
*payload = (char *)calloc(sizeof(char), MAX_PAYLOAD_LENGTH);
if (*payload == NULL)
return false;
if (opt_payload_file) {
if (get_payload_from_file(payload)) {
ret = 1;
ret = true;
}
} else if (opt_payload) {
get_payload_from_opt(payload);
ret = 1;
ret = true;
} else {
get_payload_from_stdin(payload);
ret = 1;
ret = true;
}
if (ret == 0) {
if (ret == false) {
free(*payload);
*payload = NULL;
}
out1:
return ret;
}
int instanciate_record(struct telem_ref **t_ref, char *payload)
static int instanciate_record(struct telem_ref **t_ref, char *payload)
{
int ret = 0;
@@ -341,18 +347,18 @@ int instanciate_record(struct telem_ref **t_ref, char *payload)
goto out1;
}
if ((ret = opt_event_id && tm_set_event_id(*t_ref, opt_event_id)) < 0) {
goto out1;
if (opt_event_id) {
if ((ret = tm_set_event_id(*t_ref, opt_event_id)) < 0) {
goto out1;
}
}
if ((ret = tm_set_payload(*t_ref, payload)) < 0) {
goto out1;
}
ret = tm_set_payload(*t_ref, payload);
out1:
return ret;
}
int send_record(char *payload)
static int send_record(char *payload)
{
struct telem_ref *t_ref = NULL;
int ret = 0;
@@ -362,6 +368,9 @@ int send_record(char *payload)
}
if ((ret = tm_send_record(t_ref)) < 0) {
if (ret == -ECONNREFUSED) {
fprintf(stderr, "Unable to send record. Make sure to opt-in to telemetry first 'telemctl opt-in'\n");
}
goto out;
}
@@ -371,20 +380,20 @@ out:
return ret;
}
int print_record(char *payload)
static int print_record(char *payload)
{
struct telem_ref *t_ref = NULL;
int ret = 0;
int i = 0;
int ret;
if ((ret = instanciate_record(&t_ref, payload)) == 0) {
int i;
for (i = 0; i < NUM_HEADERS; i++) {
fprintf(stdout, "%s", t_ref->record->headers[i]);
}
fprintf(stdout, "%s\n", t_ref->record->payload);
tm_free_record(t_ref);
}
tm_free_record(t_ref);
return ret;
}
@@ -415,7 +424,6 @@ int main(int argc, char **argv)
ret = EXIT_SUCCESS;
fail:
free(config_file);
free(opt_class);
free(opt_payload);
free(opt_event_id);
+52 -12
View File
@@ -202,9 +202,15 @@ void transmit_spooled_record(char *record_path, bool *post_succeeded, long size)
FILE *fp = NULL;
char *headers[NUM_HEADERS];
char *payload = NULL;
int i, k;
char line[LINE_MAX] = { 0 };
int num_headers = 0, k;
#if (LINE_MAX > PATH_MAX)
char line[LINE_MAX+1] = { 0 };
#else
char line[PATH_MAX+1] = { 0 };
#endif
long offset;
char *cfg_file = NULL;
uint32_t cfg_prefix = 0;
fp = fopen(record_path, "r");
if (fp == NULL) {
@@ -212,16 +218,47 @@ void transmit_spooled_record(char *record_path, bool *post_succeeded, long size)
return;
}
for (i = 0; i < NUM_HEADERS; i++) {
const char *header_name = get_header_name(i);
if (!fgets(line, LINE_MAX, fp)) {
// First line optionally contains configuration file path
if (fread(&cfg_prefix, CFG_PREFIX_LENGTH, 1, fp) != 1) {
telem_log(LOG_ERR, "Error while parsing spooled record configuration info.\n");
goto read_error;
}
if (cfg_prefix == CFG_PREFIX_32BIT) {
size_t pathlen;
char *nl;
if (!fgets(line, sizeof(line), fp)) {
telem_log(LOG_ERR, "Error while parsing record file\n");
fclose(fp);
return;
goto read_error;
}
if ((nl = strchr(line, '\n')) != NULL) {
*nl = '\0';
}
pathlen = strlen(line);
cfg_file = malloc(pathlen + 1);
if (cfg_file == NULL) {
telem_log(LOG_ERR, "Could not allocate memory for config file path\n");
goto read_error;
}
strcpy (cfg_file, line);
telem_debug("DEBUG: cfg_file: %s\n", cfg_file);
} else {
cfg_file = NULL;
rewind(fp);
}
for (num_headers = 0; num_headers < NUM_HEADERS; num_headers++) {
const char *header_name = get_header_name(num_headers);
if (!fgets(line, sizeof(line), fp)) {
telem_log(LOG_ERR, "Error while parsing record file\n");
goto read_error;
}
//Get rid of trailing newline
strtok(line, "\n");
if (get_header(line, header_name, &headers[i])) {
if (get_header(line, header_name, &headers[num_headers])) {
continue;
} else {
telem_log(LOG_ERR, "transmit_spooled_record: Incorrect"
@@ -242,8 +279,7 @@ void transmit_spooled_record(char *record_path, bool *post_succeeded, long size)
if (!payload) {
telem_log(LOG_ERR, "Could not allocate memory for payload\n");
fclose(fp);
return;
goto read_error;
}
memset(payload, 0, (size_t)size);
@@ -254,7 +290,7 @@ void transmit_spooled_record(char *record_path, bool *post_succeeded, long size)
goto read_error;
}
*post_succeeded = post_record_http(headers, payload);
*post_succeeded = post_record_http(headers, payload, cfg_file);
if (*post_succeeded) {
unlink(record_path);
}
@@ -267,9 +303,13 @@ read_error:
fclose(fp);
}
for (k = 0; k < i; k++) {
for (k = 0; k < num_headers; k++) {
free(headers[k]);
}
if (cfg_file) {
free(cfg_file);
}
}
int spool_record_compare(const void *entrya, const void *entryb, void *path)
+555
View File
@@ -0,0 +1,555 @@
/*
* This program is part of the Clear Linux Project
*
* Copyright 2019 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 <stdbool.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <limits.h>
#include <ftw.h>
#include <unistd.h>
#include <locale.h>
#include <pwd.h>
#include <grp.h>
#include <errno.h>
#define TELEM_DIR "/etc/telemetrics"
#define TM_OPT_IN TELEM_DIR"/opt-in"
#define TELEM_WRK_DIRS_CONF "/usr/lib/tmpfiles.d/telemetrics-dirs.conf"
#define cmd_create_tmp_files "systemd-tmpfiles --create " TELEM_WRK_DIRS_CONF
#define cmd_mk_telemdir "mkdir -p " TELEM_DIR
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
static char *SPECIAL_UNITS[] = {
"hprobe.timer",
"telemprobd.socket",
"telempostd.path",
"klogscanner.service",
"journal-probe-tail.service",
};
#define NUM_SPECIAL_UNITS ARRAY_SIZE(SPECIAL_UNITS)
static char *SERVICES[] = {
"hprobe.service",
"pstore-probe.service",
"telemprobd.service",
"telempostd.service",
"journal-probe.service",
};
#define NUM_SERVICES ARRAY_SIZE(SERVICES)
static int telemctl_start(void);
static int telemctl_stop(void);
static int telemctl_restart(void);
static int telemctl_is_active(void);
static int telemctl_opt_out(void);
static int telemctl_opt_in(void);
static int telemctl_journal(char *);
struct telemcmd {
bool root;
char *cmd;
union {
int (*f1)(void);
int (*f2)(char *);
} f;
char *doc;
};
static struct telemcmd commands[] = {
{true, "stop", {.f1=telemctl_stop}, "Stops all running telemetry services" },
{true, "start", {.f1=telemctl_start}, "Starts all telemetry services" },
{true, "restart", {.f1=telemctl_restart}, "Restarts all telemetry services" },
{false, "is-active", {.f1=telemctl_is_active},"Checks if telemprobd and telempostd are active" },
{true, "opt-in", {.f1=telemctl_opt_in}, "Opts in to telemetry, and starts telemetry services" },
{true, "opt-out", {.f1=telemctl_opt_out}, "Opts out of telemetry, and stops telemetry services" },
{true, "journal", {.f2=telemctl_journal}, "Prints telemetry journal contents. Use -h argument with\n command for more options"}
};
static int syscmd(char *cmd, char *buff, int bufflen)
{
int status;
#ifdef DEBUG
printf("[debug] [%s] %s\n", __func__, cmd);
#endif
FILE *fp = popen(cmd, "r");
if (fp == NULL) {
perror("popen");
return -1;
}
while (fgets(buff, bufflen, fp) != NULL) {
;
}
status = pclose(fp);
if (status == -1) {
perror("pclose");
}
return status;
}
/*
Script code:
for_each_service() {
local action=$1 && shift
local -a array=($*)
for service in "${array[@]}"; do
systemctl $action $service
[ $? -ne 0 ] && notice "Failed to $action ${service}. Continuing..."
done
}
*/
static int for_each_service(const char *command, char* services[], int numservices)
{
char buff[512];
char cmd[256];
int ret = 0, status;
for (int i = 0; i < numservices; i++) {
snprintf(cmd, sizeof(cmd), "systemctl %s %s", command, services[i]);
memset(buff, 0, sizeof(buff));
status = syscmd(cmd, buff, sizeof(buff));
if (status != 0 || buff[0] != 0) {
fprintf(stderr, "%s", buff);
fprintf(stderr, "Failed to %s %s. Continuing...", command, services[i]);
ret = 1;
}
}
return ret;
}
/*
Script code:
telem_start() {
[ -f $OPT_OUT_FILE ] && exit_err "Opt out is enabled. Cannot start services."
create_work_dirs
for_each_service "start" ${SPECIAL_UNITS[@]}
}
Modified to add explicit opt-in
*/
static int telemctl_start(void)
{
char buff[512];
int status, ret;
if (access(TM_OPT_IN, F_OK) != 0) {
fprintf(stderr, "Opt in to telemetry first.\n");
return 1;
}
/* Creates dirs if missing, adjust ownership if exists */
memset(buff, 0, sizeof(buff));
/* Execute "systemd-tmpfiles --create ${TELEM_WRK_DIRS_CONF}" */
status = syscmd(cmd_create_tmp_files, buff, sizeof(buff));
fprintf(stderr, "%s", buff);
if (status == -1) {
return 1;
}
ret = for_each_service("start", SPECIAL_UNITS, NUM_SPECIAL_UNITS);
return ret;
}
/*
Script code:
telem_stop() {
for_each_service "stop" ${SPECIAL_UNITS[@]}
for_each_service "stop" ${SERVICES[@]}
}
*/
static int telemctl_stop(void)
{
int ret;
/* the special units must be stopped first so that activation no longer happens */
ret = for_each_service("stop", SPECIAL_UNITS, NUM_SPECIAL_UNITS);
ret |= for_each_service("stop", SERVICES, NUM_SERVICES);
return ret;
}
/*
Script code:
*
telem_restart() {
telem_stop
telem_start
}
*/
static int telemctl_restart(void)
{
if (telemctl_stop() == 0) {
return telemctl_start();
}
return 1;
}
/*
Script code:
telem_is_active() {
echo "telemprobd :" $(systemctl is-active telemprobd.socket)
echo "telempostd :" $(systemctl is-active telempostd.path)
}
*/
static int telemctl_is_active(void)
{
char buff[256];
memset(buff, 0, sizeof(buff));
if (syscmd("systemctl is-active telemprobd.socket", buff, sizeof(buff)) != -1) {
printf("telemprobd : %s", buff);
memset(buff, 0, sizeof(buff));
if (syscmd("systemctl is-active telempostd.path",buff, sizeof(buff)) != -1) {
printf("telempostd : %s", buff);
return 0;
}
}
return 1;
}
static int unlink_cb(const char *fpath, const struct stat *sb, int typeflag, struct FTW *ftwbuf)
{
int rv = remove(fpath);
#ifdef DEBUG
fprintf(stderr, "[debug] [%s] remove fpath:%s rv:%d\n", __func__, fpath, rv);
#endif
return rv;
}
/* rm -rf path
*
* We run as root, so be extra careful when deleting folders.
* We only delete folders owned by telemetry:telemetry.
* As for the files, they can be owned by root.
*/
static int rm_rf(char *path)
{
struct stat info;
if (stat(path, &info) == 0) {
struct passwd *pw = getpwuid(info.st_uid);
if (pw == NULL) {
perror("getpwuid");
return -1;
}
if (strcmp(pw->pw_name, "telemetry") != 0) {
fprintf(stderr, "Not removing \"%s\": Incorrect folder owner \"%s\"\n",
path, pw->pw_name);
return -1;
}
struct group *gr = getgrgid(info.st_gid);
if (gr == NULL) {
perror("getgrgid");
return -1;
}
if (strcmp(gr->gr_name, "telemetry") != 0) {
fprintf(stderr, "Not removing \"%s\": Incorrect folder group \"%s\"\n",
path, gr->gr_name);
return -1;
}
return nftw(path, unlink_cb, 64, FTW_DEPTH | FTW_PHYS);
} else {
// Some folders are subfolders of folders already recursivley deleted.
// So they may not exist anymore.
if (errno != ENOENT) {
fprintf(stderr, "file: %s :", path);
perror("stat");
return -1;
}
return 0;
}
}
/*
Script code:
telem_remove_work_dirs() {
# Remove dirs
awk '/^d/{print $2}' ${TELEM_WRK_DIRS_CONF} | xargs rm -rf;
}
Notes:
Parse /usr/lib/tmpfiles.d/telemetrics-dirs.conf
This file was used to create the folders needed by telemetry.
Now we want to delete them. The file contents is as:
d /usr/local/var/lib/telemetry 0755 telemetry telemetry -
d /usr/local/var/spool/telemetry 0750 telemetry telemetry -
d /usr/local/var/log/telemetry 0750 telemetry telemetry -
d /usr/local/var/log/telemetry/records 0750 telemetry telemetry -
d /usr/local/var/cache/telemetry 0750 telemetry telemetry -
d /usr/local/var/cache/telemetry/pstore 0750 telemetry telemetry -
*/
static int telemctl_remove_work_dirs(void)
{
FILE *fp = fopen(TELEM_WRK_DIRS_CONF, "r");
if (fp == NULL){
fprintf(stderr, "Could not open file %s", TELEM_WRK_DIRS_CONF);
return 1;
}
while (true) {
char folder[PATH_MAX+1];
char line[PATH_MAX+30];
if (fgets(line, sizeof(line), fp) == NULL) {
if (ferror(fp)) {
fprintf(stderr, "Error reading file %s\n", TELEM_WRK_DIRS_CONF);
fclose(fp);
return 1;
}
// EOF
break;
}
int ret = sscanf(line, "d %s %*s telemetry telemetry - \n", folder);
if (ret == 1) {
/* Delete the folder. We may get an error trying to delete
* folders already deleted. We ignore them, so don't bother
* checking rmrtf return value */
rm_rf(folder);
}
}
fclose(fp);
return 0;
}
static int mk_telem_dir(void)
{
char buff[512];
int status;
struct stat sb;
if (stat(TELEM_DIR, &sb) == 0 && S_ISDIR(sb.st_mode)) {
#ifdef DEBUG
printf("[debug] [%s] Folder %s exists\n", __func__, TELEM_DIR);
#endif
return 0;
}
memset(buff, 0, sizeof(buff));
/* Execute "mkdir -p TM_DIR" */
status = syscmd(cmd_mk_telemdir, buff, sizeof(buff));
fprintf(stderr, "%s", buff);
if (status == -1 || buff[0] != 0) {
return 1;
}
return 0;
}
/*
Script code:
telem_opt_out() {
[ -f $OPT_OUT_FILE ] && exit_ok "Already opted out. Nothing to do."
mkdir -p $TELEM_DIR || exit_err "Failed to create ${TELEM_DIR}."
touch $OPT_OUT_FILE || exit_err "Failed to create ${OPT_OUT_FILE}."
telem_stop
telem_remove_work_dirs
}
*/
static int telemctl_opt_out(void)
{
int ret;
/* Remove TM_OPT_IN file */
if (unlink(TM_OPT_IN) != 0) {
if (errno == ENOENT) {
fprintf(stderr, "Already opted out. Nothing to do.\n");
return 0;
}
fprintf(stderr, "Failed to remove %s.\n", TM_OPT_IN);
return 1;
}
ret = telemctl_stop();
ret |= telemctl_remove_work_dirs();
return ret;
}
/*
Script code:
telem_opt_in() {
[ ! -f $OPT_OUT_FILE ] && exit_ok "Already opted in. Nothing to do."
rm -f $OPT_OUT_FILE || exit_err "Failed to remove ${OPT_OUT_FILE}."
telem_start
}
Modified to add explicit opt-in
*/
static int telemctl_opt_in(void)
{
/* Ensure TELEM_DIR exists */
if (mk_telem_dir() != 0) {
fprintf(stderr, "Failed to create %s\n", TELEM_DIR);
return 1;
}
/* Create a brand new file TM_OPT_IN, we may fail because the file exists already.
* In that case we are already opted in and we are done here. */
int fd = open(TM_OPT_IN, O_CREAT|O_EXCL|O_WRONLY, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
if (fd == -1) {
if (errno == EEXIST) {
fprintf(stderr, "Already opted in. Nothing to do.\n");
return 0;
} else {
fprintf(stderr, "Failed to create %s.\n", TM_OPT_IN);
return 1;
}
}
close(fd);
return 0;
}
static int journal_cmd(char *cmd)
{
int status;
char buff[256];
#ifdef DEBUG
printf("[debug] [%s] %s\n", __func__, cmd);
#endif
FILE *fp = popen(cmd, "r");
if (fp == NULL) {
perror("popen");
return -1;
}
while (fgets(buff, sizeof(buff), fp) != NULL) {
printf("%s", buff);
}
status = pclose(fp);
if (status == -1) {
perror("pclose");
}
return status;
}
static char* concatargs(int argc, char** argv)
{
size_t len = 1 + strlen("telem_journal ");
for (int i = 2; i < argc; i++) {
len += strlen(argv[i]) + 1;
}
char *buff = malloc(len*sizeof(char));
if (buff != NULL) {
buff[0] = '\0';
strcat(buff,"telem_journal ");
for (int i = 2; i < argc; i++) {
strcat(buff, argv[i]);
strcat(buff, " ");
}
}
return buff;
}
static int telemctl_journal(char * x)
{
journal_cmd(x);
return 1;
}
static void print_usage(char *str)
{
printf("%s - Control actions for telemetry services\n\n", str);
for (int i = 0; i < sizeof(commands)/sizeof(commands[0]); i++) {
printf(" %-9s %s\n", commands[i].cmd, commands[i].doc);
}
}
int main(int argc, char **argv)
{
int i, ret = EXIT_SUCCESS;
bool is_root;
setlocale(LC_ALL, "");
if (argc == 1) {
print_usage(argv[0]);
exit(2);
}
is_root = getuid()?false:true;
for (i = 0; i < sizeof(commands)/sizeof(commands[0]); i++) {
if (strcmp(commands[i].cmd, argv[1]) == 0) {
if (commands[i].root == true && is_root == false) {
fprintf(stderr, "Must be root to run this command. Exiting...\n");
exit(1);
}
/* Special treatment for "journal", it can have cmd line arguments */
if (strcmp(argv[1],"journal") != 0) {
if (argc != 2) {
print_usage(argv[0]);
exit(2);
}
ret = commands[i].f.f1();
} else {
char *buff = concatargs(argc, argv);
if (buff != NULL) {
ret = commands[i].f.f2(buff);
free(buff);
}
}
break;
}
}
if (i == sizeof(commands)/sizeof(commands[0])) {
printf("Unknown command passed to %s\n\n", argv[0]);
print_usage(argv[0]);
exit(2);
}
exit(ret);
}
+158 -52
View File
@@ -36,7 +36,9 @@
#include "log.h"
#include "configuration.h"
void initialize_daemon(TelemDaemon *daemon)
static void process_record(TelemDaemon *daemon, client *cl);
void initialize_probe_daemon(TelemDaemon *daemon)
{
client_list_head head;
LIST_INIT(&head);
@@ -80,7 +82,8 @@ bool is_client_list_empty(client_list_head *client_head)
return (client_head->lh_first == NULL);
}
void terminate_client(TelemDaemon *daemon, client *cl, nfds_t index)
static void terminate_client(TelemDaemon *daemon, client *cl, nfds_t index)
{
/* Remove fd from the pollfds array */
del_pollfd(daemon, index);
@@ -91,24 +94,40 @@ void terminate_client(TelemDaemon *daemon, client *cl, nfds_t index)
remove_client(&(daemon->client_head), cl);
}
bool handle_client(TelemDaemon *daemon, nfds_t ind, client *cl)
/*
See "tm_send_record" for record retails.
recv buffer layout:
* <uint32_t record_size> : so recv knows how much to read
* <custom cfg file field> : optional, variable size (string)
* <uint32_t header_size>
* <headers + Payload>
* <null-byte>
The routine handle_client only cares about "record_size".
However, we need to validate if the record_size is reasonable. We assume the
worst case scenario would be a record with max cfg file field. There is no
exact way to determine header_size, so we assume each line at most 80 chars.
*/
#define MAX_RECORD_SIZE (2*sizeof(uint32_t) + CFG_PREFIX_LENGTH + PATH_MAX + \
MAX_PAYLOAD_LENGTH + NUM_HEADERS*80)
bool handle_client(TelemDaemon *daemon, nfds_t index, client *cl)
{
/* For now read data from fd */
ssize_t len;
size_t record_size = 0;
size_t buf_size;
bool processed = false;
uint32_t record_size;
if (!cl->buf) {
cl->buf = malloc(RECORD_SIZE_LEN);
cl->size = RECORD_SIZE_LEN;
}
if (!cl->buf) {
telem_log(LOG_ERR, "Unable to allocate memory, exiting\n");
exit(EXIT_FAILURE);
if (cl->buf != NULL) {
free(cl->buf);
cl->buf = NULL;
}
malloc_trim(0);
len = recv(cl->fd, cl->buf, RECORD_SIZE_LEN, MSG_PEEK | MSG_DONTWAIT);
len = recv(cl->fd, &record_size, RECORD_SIZE_LEN, MSG_PEEK | MSG_DONTWAIT);
if (len < 0) {
telem_log(LOG_ERR, "Failed to talk to client %d: %s\n", cl->fd,
strerror(errno));
@@ -120,6 +139,39 @@ bool handle_client(TelemDaemon *daemon, nfds_t ind, client *cl)
goto end_client;
}
/* Read the record size first */
len = recv(cl->fd, &record_size, RECORD_SIZE_LEN, 0);
if (len < 0) {
telem_log(LOG_ERR, "Failed to receive data from client"
" %d: %s\n", cl->fd, strerror(errno));
goto end_client;
} else if (len == 0) {
telem_log(LOG_DEBUG, "End of transmission for client"
" %d\n", cl->fd);
goto end_client;
}
/* Now that we know the record size, allocate a new buffer
* for the record body. We don't need to record size itself in the body.
*/
if (record_size <= RECORD_SIZE_LEN || record_size > MAX_RECORD_SIZE) {
telem_log(LOG_ERR, "Record size %u greater tham maximum allowed %lu."
"Recored ignored\n", record_size,
MAX_RECORD_SIZE);
goto end_client;
}
buf_size = record_size - RECORD_SIZE_LEN;
cl->buf = calloc(1, buf_size);
if (!cl->buf) {
telem_log(LOG_ERR, "Unable to allocate memory, exiting\n");
exit(EXIT_FAILURE);
}
cl->size = buf_size;
cl->offset = 0;
/* Read the actual record*/
do {
malloc_trim(0);
len = recv(cl->fd, cl->buf + cl->offset, cl->size - cl->offset, 0);
@@ -134,45 +186,20 @@ bool handle_client(TelemDaemon *daemon, nfds_t ind, client *cl)
}
cl->offset += (size_t)len;
if (cl->offset < RECORD_SIZE_LEN) {
continue;
}
if (cl->size == RECORD_SIZE_LEN) {
record_size = *(uint32_t *)(cl->buf);
telem_log(LOG_DEBUG, "Total size of record: %zu\n", record_size);
if (record_size == 0) { //record_size < RECORD_MIN_SIZE || record_size > RECORD_MAX_SIZE
goto end_client;
}
// We just processed the record size field, so the remaining format
// is (header size field + record body + terminating '\0' byte)
cl->size = sizeof(uint32_t) + record_size + 1;
cl->buf = realloc(cl->buf, cl->size);
memset(cl->buf, 0, cl->size);
cl->offset = 0;
if (!cl->buf) {
telem_log(LOG_ERR, "Unable to allocate memory, exiting\n");
exit(EXIT_FAILURE);
}
}
if (cl->offset != cl->size) {
/* full record not received yet */
continue;
}
if (cl->size != RECORD_SIZE_LEN) {
/* entire record has been received */
if (cl->offset == cl->size) {
process_record(daemon, cl);
/* TODO: cleanup or terminate? */
cl->offset = 0;
cl->size = RECORD_SIZE_LEN;
free(cl->buf);
cl->buf = NULL;
processed = true;
telem_log(LOG_DEBUG, "Record processed for client %d\n", cl->fd);
telem_debug("DEBUG: Record processed for client %d\n", cl->fd);
break;
}
} while (len > 0);
end_client:
telem_log(LOG_DEBUG, "Processed client %d: %s\n", cl->fd, processed ? "true" : "false");
terminate_client(daemon, cl, ind);
terminate_client(daemon, cl, index);
return processed;
}
@@ -222,14 +249,14 @@ char *read_machine_id_override()
return machine_override;
}
void machine_id_replace(char **machine_header, char *machine_id_override)
static void machine_id_replace(char **machine_header, char *machine_id_override)
{
char machine_id[33] = { 0 };
char *old_header;
int ret;
if (machine_id_override) {
strcpy(machine_id, machine_id_override);
strncpy(machine_id, machine_id_override, sizeof(machine_id)-1);
} else {
if (!get_machine_id(machine_id)) {
// TODO: decide if error handling is needed here
@@ -247,7 +274,58 @@ void machine_id_replace(char **machine_header, char *machine_id_override)
free(old_header);
}
void process_record(TelemDaemon *daemon, client *cl)
static void stage_record(char *filepath, char *headers[], char *body, char *cfg_file)
{
int tmpfd;
FILE *tmpfile = NULL;
telem_debug("DEBUG: filepath:%s\n", filepath);
telem_debug("DEBUG: body:%s\n", body);
telem_debug("DEBUG: cfg:%s\n", cfg_file);
if (filepath == NULL) {
telem_log(LOG_ERR, "filepath value must be provided, aborting\n");
exit(EXIT_FAILURE);
}
tmpfd = mkstemp(filepath);
if (tmpfd < 0) {
telem_perror("Error opening staging file");
goto clean_exit;
}
// access the opened file as a stream
tmpfile = fdopen(tmpfd, "a");
if (!tmpfile) {
telem_perror("Error opening temp stage file");
close(tmpfd);
if (unlink(filepath)) {
telem_perror("Error deleting temp stage file");
}
goto clean_exit;
}
// write cfg info if exists
if (cfg_file != NULL) {
fprintf(tmpfile, "%s%s\n", CFG_PREFIX, cfg_file);
}
// write headers
for (int i = 0; i < NUM_HEADERS; i++) {
fprintf(tmpfile, "%s\n", headers[i]);
}
// write body
fprintf(tmpfile, "%s\n", body);
fflush(tmpfile);
fclose(tmpfile);
clean_exit:
return;
}
static void process_record(TelemDaemon *daemon, client *cl)
{
int i = 0;
int ret = 0;
@@ -259,11 +337,39 @@ void process_record(TelemDaemon *daemon, client *cl)
char *msg;
char *body;
char *recordpath = NULL;
char *cfg_file = NULL;;
size_t cfg_info_size = 0;
uint8_t *buf;
header_size = *(uint32_t *)cl->buf;
message_size = cl->size - header_size;
assert(message_size > 0); //TODO:Check for min and max limits
msg = (char *)cl->buf + sizeof(uint32_t);
buf = cl->buf;
/* Check for an optional CFG_PREFIX in the first 32 bits */
if (*(uint32_t *)buf == CFG_PREFIX_32BIT) {
char *cfg = (char *)cl->buf;
cfg_file = cfg + CFG_PREFIX_LENGTH;
cfg_info_size = CFG_PREFIX_LENGTH + strlen(cfg_file) + 1;
telem_debug("DEBUG: cfg_file: %s\n", cfg_file);
}
buf += cfg_info_size;
header_size = *(uint32_t *)buf;
/* Header size can not be bigger than buffer size bail out early */
if ((uint32_t)header_size >= (uint32_t)cl->size) {
return;
}
message_size = cl->size - (cfg_info_size + header_size);
telem_debug("DEBUG: cl->size: %ld\n", cl->size);
telem_debug("DEBUG: header_size: %ld\n", header_size);
telem_debug("DEBUG: message_size: %ld\n", message_size);
telem_debug("DEBUG: cfg_info_size: %ld\n", cfg_info_size);
telem_debug("Total: %zu\n", header_size + cfg_info_size + message_size);
/* Check message size bounds */
if (message_size <= 0 || message_size > MAX_PAYLOAD_LENGTH) {
telem_log(LOG_INFO, "Record message size out of bounds\n");
return;
}
msg = (char *)buf + sizeof(uint32_t);
/* Copying the headers as strtok modifies the orginal buffer */
temp_headers = strndup(msg, header_size);
@@ -293,7 +399,7 @@ void process_record(TelemDaemon *daemon, client *cl)
exit(EXIT_FAILURE);
}
stage_record(recordpath, headers, body);
stage_record(recordpath, headers, body, cfg_file);
free(recordpath);
end:
free(temp_headers);
@@ -356,7 +462,7 @@ bool get_machine_id(char *machine_id)
return false;
}
ret = fscanf(id_file, "%s", machine_id);
ret = fscanf(id_file, "%32s", machine_id);
if (ret != 1) {
telem_perror("Could not read machine id from file");
fclose(id_file);
+1 -22
View File
@@ -62,7 +62,7 @@ typedef struct TelemDaemon {
*
* @param daemon A pointer to the daemon structure.
*/
void initialize_daemon(TelemDaemon *daemon);
void initialize_probe_daemon(TelemDaemon *daemon);
/**
* Add poll fd struct to the array of pollfds.
@@ -125,27 +125,6 @@ void remove_client(client_list_head *client_head, client *cl);
*/
bool is_client_list_empty(client_list_head *client_head);
/**
* Terminate a client connection
*
* @param daemon Pointer to the daemon
* @param cl Pointer to the client in the client list
* @param index The index of the client file descriptor in the poll fd
* array
*
*/
void terminate_client(TelemDaemon *daemon, client *cl, nfds_t index);
/**
* Process record from a client
*
* @param daemon Pointer to the daemon
* @param cl Pointer to the client in the client list
*
* @return true on success, false on failure
*/
void process_record(TelemDaemon *daemon, client *cl);
/**
* Get random machine id stored in file
*
+108 -122
View File
@@ -35,6 +35,7 @@
#include "common.h"
#include "configuration.h"
#include "telemetry.h"
#include "log.h"
/**
* Return a file descriptor to either site's version file
@@ -76,9 +77,7 @@ static int set_header(char **dest, const char *prefix, char *value, size_t *head
rc = asprintf(dest, "%s: %s\n", prefix, value);
if (rc < 0) {
#ifdef DEBUG
fprintf(stderr, "CRIT: Out of memory\n");
#endif
telem_log(LOG_CRIT, "CRIT: Out of memory\n");
return -ENOMEM;
} else {
*header_size += (size_t)rc;
@@ -233,35 +232,49 @@ static int set_system_name_header(struct telem_ref *t_ref)
int fd;
char buf[SMALL_LINE_BUF] = { 0 };
char name[SMALL_LINE_BUF] = { 0 };
char variant[SMALL_LINE_BUF] = { 0 };
fd = version_file();
if (fd == -1) {
#ifdef DEBUG
fprintf(stderr, "WARNING: Cannot find os-release file\n");
#endif
telem_log(LOG_WARNING, "WARNING: Cannot find os-release file\n");
sprintf(name, "unknown");
} else {
fs = fdopen(fd, "r");
while (fgets(buf, SMALL_LINE_BUF, fs)) {
if (sscanf(buf, "ID=%s", name) < 1) {
continue;
} else {
if (sscanf(buf, "ID=%s", name) > 0) {
break;
}
}
rewind(fs);
while (fgets(buf, SMALL_LINE_BUF, fs)) {
if (sscanf(buf, "VARIANT_ID=%s", variant) > 0) {
break;
}
}
fclose(fs);
if (strlen(name) == 0) {
#ifdef DEBUG
fprintf(stderr, "WARNING: Cannot find os-release field: ID\n");
#endif
telem_log(LOG_WARNING, "WARNING: Cannot find os-release field: ID\n");
sprintf(name, "unknown");
}
fclose(fs);
if (strlen(variant) == 0) {
snprintf(buf, sizeof(buf), "%s", name);
} else {
/* We do not want the header to be greater than 80 characters, allow truncation */
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wformat-truncation"
if (snprintf(buf, sizeof(buf), "%s_%s", name, variant) > sizeof(buf)) {
telem_log(LOG_WARNING, "WARNING: truncating system name header to 80 characters\n");
}
telem_debug("DEBUG: Found variant OS information: %s\n", variant);
#pragma GCC diagnostic pop
}
}
return set_header(&(t_ref->record->headers[TM_SYSTEM_NAME]),
TM_SYSTEM_NAME_STR, name,
TM_SYSTEM_NAME_STR, buf,
&(t_ref->record->header_size));
}
@@ -285,9 +298,7 @@ static int set_system_build_header(struct telem_ref *t_ref)
fd = version_file();
if (fd == -1) {
#ifdef DEBUG
fprintf(stderr, "WARNING: Cannot find build version file\n");
#endif
telem_log(LOG_WARNING, "WARNING: Cannot find build version file\n");
sprintf(version, "0");
} else {
fs = fdopen(fd, "r");
@@ -300,9 +311,7 @@ static int set_system_build_header(struct telem_ref *t_ref)
}
if (strlen(version) == 0) {
#ifdef DEBUG
fprintf(stderr, "WARNING: Cannot find build version number\n");
#endif
telem_log(LOG_WARNING, "WARNING: Cannot find build version number\n");
sprintf(version, "0");
}
@@ -416,7 +425,7 @@ static int set_cpu_model_header(struct telem_ref *t_ref)
}
} else {
model_name = "blank";
fprintf(stderr, "NOTICE: Unable to find attribute:%s\n", attr_name);
telem_log(LOG_NOTICE, "NOTICE: Unable to find attribute:%s\n", attr_name);
}
status = set_header(
@@ -425,9 +434,7 @@ static int set_cpu_model_header(struct telem_ref *t_ref)
&(t_ref->record->header_size));
} else {
#ifdef DEBUG
fprintf(stderr, "NOTICE: Unable to open /proc/cpuinfo\n");
#endif
telem_log(LOG_NOTICE, "NOTICE: Unable to open /proc/cpuinfo\n");
status = -1;
}
@@ -461,9 +468,7 @@ static int get_dmi_value(const char *source, const char *key, char **buf)
*buf = (char *)malloc(sizeof(char) * SMALL_LINE_BUF);
if (*buf == NULL) {
#ifdef DEBUG
fprintf(stderr, "CRIT: Out of memory\n");
#endif
telem_log(LOG_CRIT, "CRIT: Out of memory\n");
return -ENOMEM;
}
@@ -486,9 +491,7 @@ static int get_dmi_value(const char *source, const char *key, char **buf)
if (new_size == 0) {
old_value = *buf;
if (asprintf(buf, "blank") < 0) {
#ifdef DEBUG
fprintf(stderr, "CRIT: Out of memory\n");
#endif
telem_log(LOG_CRIT, "CRIT: Out of memory\n");
ret = -ENOMEM;
}
free(old_value);
@@ -505,24 +508,18 @@ static int get_dmi_value(const char *source, const char *key, char **buf)
if (j == new_size) {
old_value = *buf;
if (asprintf(buf, "blank") < 0) {
#ifdef DEBUG
fprintf(stderr, "CRIT: Out of memory\n");
#endif
telem_log(LOG_CRIT, "CRIT: Out of memory\n");
ret = -ENOMEM;
}
free(old_value);
}
}
} else {
#ifdef DEBUG
fprintf(stderr, "NOTICE: Dmi file %s does not exist\n", source);
#endif
telem_debug("DEBUG: Dmi file %s does not exist\n", source);
old_value = *buf;
if (asprintf(buf, "no_%s_file", key) < 0) {
#ifdef DEBUG
fprintf(stderr, "CRIT: Out of memory\n");
#endif
telem_log(LOG_CRIT, "CRIT: Out of memory\n");
ret = -ENOMEM;
}
free(old_value);
@@ -916,28 +913,16 @@ int tm_create_record(struct telem_ref **t_ref, uint32_t severity,
char *classification, uint32_t payload_version)
{
int ret = 0;
int k = 0;
struct stat unused;
k = stat(TM_OPT_OUT_FILE, &unused);
if (k == 0) {
// Bail early if opt-out is enabled
return -ECONNREFUSED;
}
*t_ref = (struct telem_ref *)malloc(sizeof(struct telem_ref));
if (*t_ref == NULL) {
#ifdef DEBUG
fprintf(stderr, "CRIT: Out of memory\n");
#endif
telem_log(LOG_CRIT, "CRIT: Out of memory\n");
return -ENOMEM;
}
(*t_ref)->record = (struct telem_record *)malloc(sizeof(struct telem_record));
if ((*t_ref)->record == NULL) {
#ifdef DEBUG
fprintf(stderr, "CRIT: Out of memory\n");
#endif
telem_log(LOG_CRIT, "CRIT: Out of memory\n");
free(*t_ref);
return -ENOMEM;
}
@@ -991,38 +976,25 @@ int tm_set_payload(struct telem_ref *t_ref, char *payload)
{
size_t payload_len;
int ret = 0;
int k = 0;
struct stat unused;
k = stat(TM_OPT_OUT_FILE, &unused);
if (k == 0) {
// Bail early if opt-out is enabled
return -ECONNREFUSED;
}
payload_len = strlen((char *)payload);
if (payload_len > MAX_PAYLOAD_LENGTH) {
if (payload == NULL) {
telem_log(LOG_WARNING, "payload pointer is NULL\n");
return -EINVAL;
}
payload_len = strnlen(payload, MAX_PAYLOAD_LENGTH);
if (payload_is_ascii(payload, payload_len) != 0) {
return -EINVAL;
}
t_ref->record->payload = (char *)malloc(sizeof(char) * payload_len + 1);
t_ref->record->payload = strndup(payload, payload_len);
if (!t_ref->record->payload) {
#ifdef DEBUG
fprintf(stderr, "CRIT: Out of memory\n");
#endif
telem_log(LOG_CRIT, "CRIT: Out of memory\n");
return -ENOMEM;
}
memset(t_ref->record->payload, 0, sizeof(char) * payload_len + 1);
strncpy((char *)(t_ref->record->payload), (char *)payload, payload_len);
t_ref->record->payload_size = payload_len;
return ret;
@@ -1066,7 +1038,7 @@ 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) {
if (t_ref && t_ref->record) {
// free default id before overriding
free(t_ref->record->headers[TM_EVENT_ID]);
// set new event_id
@@ -1102,10 +1074,7 @@ static int tm_write_socket(int fd, char *buf, size_t nbytes)
if (b == -1 && errno != EAGAIN) {
ret = -errno;
#ifdef DEBUG
fprintf(stderr, "ERR: Write to daemon socket with"
" system error: %s\n", strerror(errno));
#endif
telem_perror("Error writing to daemon socket");
return ret;
} else if (b == -1 &&
(errno == EAGAIN || errno == EWOULDBLOCK)) {
@@ -1145,10 +1114,7 @@ static int tm_get_socket(void)
if (sfd == -1) {
ret = -errno;
#ifdef DEBUG
fprintf(stderr, "ERR: Attempt to allocate socket fd failed:"
" %s\n", strerror(errno));
#endif
telem_perror("Attempt to allocate socket fd failed");
return ret;
}
@@ -1159,10 +1125,7 @@ static int tm_get_socket(void)
if (setsockopt(sfd, SOL_SOCKET, SO_RCVTIMEO, &tv,
sizeof(tv)) < 0) {
ret = -errno;
#ifdef DEBUG
fprintf(stderr, "ERR: Failed to set socket timeout:"
" %s\n", strerror(errno));
#endif
telem_perror("Failed to set socket timeout");
goto out1;
}
@@ -1218,9 +1181,7 @@ static int tm_get_socket(void)
// Set non-blocking after the connect() succeeded
if ((sflags = fcntl(sfd, F_GETFL, NULL)) < 0) {
ret = -errno;
#ifdef DEBUG
fprintf(stderr, "ERR: Failed to get socket flags\n");
#endif
telem_log(LOG_ERR, "Failed to get socket flags\n");
goto out1;
}
@@ -1228,9 +1189,7 @@ static int tm_get_socket(void)
if (fcntl(sfd, F_SETFL, sflags) < 0) {
ret = -errno;
#ifdef DEBUG
fprintf(stderr, "ERR: Failed to set socket flags\n");
#endif
telem_log(LOG_ERR, "Failed to set socket flags\n");
goto out1;
}
@@ -1241,6 +1200,17 @@ out1:
return ret;
}
int tm_is_opted_in(void)
{
struct stat unused;
if (stat(TM_OPT_IN_FILE, &unused) == -1) {
return 0;
}
return 1;
}
int tm_send_record(struct telem_ref *t_ref)
{
int i;
@@ -1250,55 +1220,77 @@ int tm_send_record(struct telem_ref *t_ref)
char *data = NULL;
size_t offset = 0;
int ret = 0;
int k = 0;
struct stat unused;
size_t cfg_file_name_size = 0;
const char *cfg_file_name = NULL;
k = stat(TM_OPT_OUT_FILE, &unused);
if (k == 0) {
// Bail early if opt-out is enabled
if (tm_is_opted_in() == 0) {
// Bail early if opt-in is not existent
return -ECONNREFUSED;
}
sfd = tm_get_socket();
if (sfd < 0) {
#ifdef DEBUG
fprintf(stderr, "ERR: Failed to get socket fd: %s\n",
strerror(-sfd));
#endif
telem_log(LOG_ERR, "Failed to get socket fd: %s\n",
strerror(-sfd));
return sfd;
}
total_size = t_ref->record->header_size + t_ref->record->payload_size;
#ifdef DEBUG
fprintf(stderr, "DEBUG: Header size : %zu\n", t_ref->record->header_size);
fprintf(stderr, "DEBUG: Payload size : %zu\n", t_ref->record->payload_size);
fprintf(stderr, "DEBUG: Total size : %zu\n", total_size);
#endif
/*
* The user may want to use a custom (cmd line specified) config file.
* If that is the case, we need to include the absolute path in the data packet.
* We optionally add the string CFG:<absolute path> as the first string
* in the data. If the receiving end sees this in the data, it should
* use the config file specified to override any default telemetrics.conf
* files.
*/
cfg_file_name = get_cmd_line_config_file();
if (cfg_file_name != NULL) {
cfg_file_name_size = strlen(cfg_file_name) + 1;
total_size += (cfg_file_name_size + CFG_PREFIX_LENGTH);
}
if (cfg_file_name != NULL) {
telem_debug("DEBUG: CFG field size : %zu\n", cfg_file_name_size + CFG_PREFIX_LENGTH);
telem_debug("DEBUG: CFG file name : %s\n", cfg_file_name);
}
telem_debug("DEBUG: Header size : %zu\n", t_ref->record->header_size);
telem_debug("DEBUG: Payload size : %zu\n", t_ref->record->payload_size);
telem_debug("DEBUG: Total size : %zu\n", total_size);
/*
* Allocating buffer for what we intend to send. Buffer layout is:
* <uint32_t total_size><uint32_t header_size><headers + Payload>
* <uint32_t record_size> : so recv knows how much to read
* <custom cfg file field> : optional
* <uint32_t header_size>
* <headers + Payload>
* <null-byte>
* The additional char at the end ensures null termination
*/
record_size = (2 * sizeof(uint32_t)) + total_size + 1;
data = malloc(record_size);
data = (char *)calloc(sizeof(char), record_size);
if (!data) {
#ifdef DEBUG
fprintf(stderr, "CRIT: Out of memory\n");
#endif
telem_log(LOG_CRIT, "CRIT: Out of memory\n");
close(sfd);
return -ENOMEM;
}
memset(data, 0, record_size);
memcpy(data, &total_size, sizeof(uint32_t));
memcpy(data, &record_size, sizeof(uint32_t));
offset += sizeof(uint32_t);
if (cfg_file_name != NULL) {
memcpy(data + offset, CFG_PREFIX, CFG_PREFIX_LENGTH);
offset += CFG_PREFIX_LENGTH;
memcpy(data + offset, cfg_file_name, cfg_file_name_size);
offset += cfg_file_name_size;
}
memcpy(data + offset, &t_ref->record->header_size, sizeof(uint32_t));
offset += sizeof(uint32_t);
@@ -1311,17 +1303,11 @@ int tm_send_record(struct telem_ref *t_ref)
memcpy(data + offset, t_ref->record->payload, t_ref->record->payload_size);
#ifdef DEBUG
fprintf(stderr, "DEBUG: Data to be sent :\n\n%s\n", data + 2 * sizeof(uint32_t));
#endif
telem_debug("DEBUG: Data to be sent :\n\n%s\n", data + 2 * sizeof(uint32_t));
if ((ret = tm_write_socket(sfd, data, record_size)) == 0) {
#ifdef DEBUG
fprintf(stderr, "INFO: Successfully sent record over the socket\n");
#endif
telem_log(LOG_INFO, "INFO: Successfully sent record over the socket\n");
} else {
#ifdef DEBUG
fprintf(stderr, "ERR: Error while writing data to socket\n");
#endif
telem_log(LOG_ERR, "Error while writing data to socket\n");
}
close(sfd);
+7
View File
@@ -120,6 +120,13 @@ int tm_set_payload(struct telem_ref *t_ref, char *payload);
*/
int tm_send_record(struct telem_ref *t_ref);
/**
* Checks if telemetry was opted in
*
* @return 1 when opt-in, or 0 when opt-out
*/
int tm_is_opted_in(void);
/**
* Release the memory allocated to a telemetrics record.
*
+5
View File
@@ -21,3 +21,8 @@ TM_4_0_0 {
global:
tm_set_event_id;
} TM_3_0_0;
TM_4_1_0 {
global:
tm_is_opted_in;
} TM_4_0_0;
+122 -45
View File
@@ -1,7 +1,7 @@
/*
* This program is part of the Clear Linux Project
*
* Copyright 2018 Intel Corporation
* Copyright 2018-2023 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
@@ -25,6 +25,7 @@
#include <stdbool.h>
#include <sys/stat.h>
#include <curl/curl.h>
#include <json-c/json.h>
#include <sys/signalfd.h>
#include "log.h"
@@ -178,7 +179,7 @@ static void initialize_record_delivery(TelemPostDaemon *daemon)
daemon->record_server_delivery_enabled = record_server_delivery_enabled_config();
}
void initialize_daemon(TelemPostDaemon *daemon)
void initialize_post_daemon(TelemPostDaemon *daemon)
{
assert(daemon);
@@ -210,16 +211,69 @@ size_t write_callback(char *ptr, size_t size, size_t nmemb, void *userdata)
return size * nmemb;
}
bool post_record_http(char *headers[], char *body)
char *create_json_message(char *tm_headers[], char *tm_payload)
{
/*
* Embed the telemetry record headers and the telemetry payload into a
* JSON object string.
*/
char *json_string = NULL;
json_object *root = json_object_new_object();
/* Add the telemetry record headers */
for (int i = 0; i < NUM_HEADERS; i++) {
/* ex: arch: x86_64 */
/* Split the header field into name/value */
strtok(tm_headers[i], ":");
json_object *value = json_object_new_string(strtok(NULL, " "));
json_object_object_add(root, tm_headers[i], value);
}
json_object *payload = json_object_new_string(tm_payload);
json_object_object_add(root, "payload", payload);
/* Keep our own copy of the json string */
json_string = strdup(json_object_to_json_string_ext(root,
JSON_C_TO_STRING_PLAIN |
JSON_C_TO_STRING_NOSLASHESCAPE));
/* Free the memory associated with the JSON objects */
json_object_put(root);
return json_string;
}
bool post_record_http(char *headers[], char *body, char *cfg)
{
CURL *curl;
int res = 0;
char *content = "Content-Type: application/text";
char *content = "Content-Type: application/json";
struct curl_slist *custom_headers = NULL;
char errorbuf[CURL_ERROR_SIZE];
char *json_body = NULL;
long http_response = 0;
const char *cert_file = get_cainfo_config();
const char *tid_header = get_tidheader_config();
const char *saved_config_file = NULL;
if (cfg != NULL) {
saved_config_file = get_config_file();
if (set_config_file(cfg) != 0) {
telem_log(LOG_ERR, "set-config_file(): Failed to set %s\n", cfg);
// If we fail to load the specified config file, do not send the
// record out. We don't want to send the record out with different
// settings than explicitly requested.
// However, report success so the record gets deleted.
res = 0;
goto Done;
}
reload_config();
telem_debug("DEBUG: override server_addr:%s\n", server_addr_config());
}
// Generate the JSON message body
json_body = create_json_message(headers, body);
// Initialize the libcurl global environment once per POST. This lets us
// clean up the environment after each POST so that when the daemon is
@@ -248,16 +302,13 @@ bool post_record_http(char *headers[], char *body)
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);
for (int i = 0; i < NUM_HEADERS; i++) {
custom_headers = curl_slist_append(custom_headers, headers[i]);
}
custom_headers = curl_slist_append(custom_headers, tid_header);
// This should be set by probes/libtelemetry in the future
custom_headers = curl_slist_append(custom_headers, content);
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, custom_headers);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, body);
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, strlen(body));
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, json_body);
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, strlen(json_body));
curl_easy_setopt(curl, CURLOPT_USE_SSL, CURLUSESSL_TRY);
if (strlen(cert_file) > 0) {
@@ -298,6 +349,22 @@ bool post_record_http(char *headers[], char *body)
curl_global_cleanup();
Done:
if (json_body) {
free(json_body);
json_body = NULL;
}
if (saved_config_file != NULL) {
if (set_config_file(saved_config_file) != 0) {
telem_log(LOG_ERR, "set-config_file(): Failed to set %s",
saved_config_file);
res = 1;
}
reload_config();
telem_debug("DEBUG: restored server_addr:%s\n", server_addr_config());
}
return res ? false : true;
}
@@ -336,8 +403,8 @@ save_err:
static void save_entry_to_journal(TelemPostDaemon *daemon, time_t t_stamp, char *headers[])
{
char *classification_value;
char *event_id_value;
char *classification_value = NULL;
char *event_id_value = NULL;
if (get_header_value(headers[TM_CLASSIFICATION], &classification_value) &&
get_header_value(headers[TM_EVENT_ID], &event_id_value)) {
@@ -405,7 +472,7 @@ static void apply_retention_policies(TelemPostDaemon *daemon, char *body)
/* Deliver record to backend if rate limiting policies are met otherwise
* spool record for future delivery */
static bool deliver_record(TelemPostDaemon *daemon, char *headers[], char *body)
static bool deliver_record(TelemPostDaemon *daemon, char *headers[], char *body, char* cfg_file)
{
bool ret = false;
@@ -427,7 +494,7 @@ static bool deliver_record(TelemPostDaemon *daemon, char *headers[], char *body)
/* Sends record if rate limiting is disabled, or all checks passed */
if (!daemon->rate_limit_enabled || (record_check_passed && byte_check_passed)) {
/* Send the record as https post */
record_sent = post_record_ptr(headers, body);
record_sent = post_record_ptr(headers, body, cfg_file);
/**
* This is the only point where an error condition could be returned
* if the record was not sent
@@ -463,22 +530,23 @@ static bool deliver_record(TelemPostDaemon *daemon, char *headers[], char *body)
return ret;
}
bool process_staged_record(char *filename, bool is_retry, TelemPostDaemon *daemon)
bool process_staged_record(char *filename, TelemPostDaemon *daemon)
{
int k;
bool ret = false;
char *headers[NUM_HEADERS];
char *body = NULL;
struct stat buf;
struct stat buf = { 0 };
time_t current_time = time(NULL);
int64_t max_spool_size = 0;
char *cfg_file = NULL;
for (k = 0; k < NUM_HEADERS; k++) {
headers[k] = NULL;
}
/** Load record **/
if ((ret = read_record(filename, headers, &body)) == false) {
if ((ret = read_record(filename, headers, &body, &cfg_file)) == false) {
telem_log(LOG_WARNING, "unable to read record\n");
ret = true; // Record corrupted? true will remove record
goto end_processing_file;
@@ -492,7 +560,7 @@ bool process_staged_record(char *filename, bool is_retry, TelemPostDaemon *daemo
}
/** Update spool directory size **/
(daemon->current_spool_size) += (buf.st_blocks * 512);
daemon->current_spool_size += (buf.st_blocks * 512);
/** Check that record is not expired **/
if (!S_ISREG(buf.st_mode) ||
@@ -502,29 +570,17 @@ bool process_staged_record(char *filename, bool is_retry, TelemPostDaemon *daemo
goto end_processing_file;
}
/* Retries should not be recorded */
if (is_retry == false) {
/** Journal entry **/
save_entry_to_journal(daemon, current_time, headers);
/** Record retention **/
apply_retention_policies(daemon, body);
}
/** Record delivery **/
if (!daemon->record_server_delivery_enabled) {
#ifdef DEBUG
telem_log(LOG_WARNING, "record server delivery disabled\n");
#endif
telem_log(LOG_INFO, "record server delivery disabled\n");
// Not an error condition
ret = true;
goto end_processing_file;
goto end_record_delivery;
}
/** Spool policies **/
if (inside_direct_spool_window(daemon, time(NULL))) {
#ifdef DEBUG
telem_log(LOG_INFO, "process_record: delivering directly to spool\n");
#endif
/* Check spool max size conf */
max_spool_size = spool_max_size_config();
if (max_spool_size != -1 &&
@@ -545,12 +601,23 @@ bool process_staged_record(char *filename, bool is_retry, TelemPostDaemon *daemo
}
/** Deliver or spool **/
ret = deliver_record(daemon, headers, body);
ret = deliver_record(daemon, headers, body, cfg_file);
end_record_delivery:
/** Save record once it is properly delivered, if record
* is spooled the record is not saved to journal until
* delievered on a re-try **/
if (ret) {
/** Save to journal **/
save_entry_to_journal(daemon, current_time, headers);
/** Record retention **/
apply_retention_policies(daemon, body);
}
end_processing_file:
/** Update spool size if record will be removed **/
if (ret) {
(daemon->current_spool_size) -= (buf.st_blocks * 512);
daemon->current_spool_size -= (buf.st_blocks * 512);
}
telem_log(LOG_DEBUG, "spool_size: %ld\n", daemon->current_spool_size);
free(body);
@@ -559,6 +626,9 @@ end_processing_file:
free(headers[k]);
}
if (cfg_file != NULL) {
free(cfg_file);
}
return ret;
}
@@ -598,7 +668,7 @@ int staging_records_loop(TelemPostDaemon *daemon)
telem_log(LOG_ERR, "Failed to allocate memory for staging record full path\n");
exit(EXIT_FAILURE);
}
if (process_staged_record(record_path, true, daemon)) {
if (process_staged_record(record_path, daemon)) {
unlink(record_path);
processed++;
}
@@ -616,7 +686,8 @@ int staging_records_loop(TelemPostDaemon *daemon)
void run_daemon(TelemPostDaemon *daemon)
{
int ret;
int retry_attempt = MAX_RETRY_ATTEMPTS;
/* retry_attempt of zero indicates we don't need to retry */
int retry_attempt = 0;
int spool_process_time = spool_process_time_config();
bool daemon_recycling_enabled = daemon_recycling_enabled_config();
time_t last_spool_run_time = time(NULL);
@@ -627,7 +698,7 @@ void run_daemon(TelemPostDaemon *daemon)
assert(daemon->pollfds[signlfd].fd);
assert(daemon->pollfds[watchfd].fd);
/* Post at boot failed initialize retries variable */
/* If we failed to send spooled records, indicate we need to retry */
if (daemon->bypass_http_post_ts != 0) {
retry_attempt = 1;
}
@@ -636,7 +707,8 @@ void run_daemon(TelemPostDaemon *daemon)
int retry_delay = spool_process_time;
malloc_trim(0);
if (retry_attempt < MAX_RETRY_ATTEMPTS) {
/* check if we need to retry sending spooled records */
if (retry_attempt > 0) {
retry_delay = retry_attempt * retry_attempt;
daemon->bypass_http_post_ts = 0;
telem_log(LOG_INFO, "Record delivery failed will retry in %d seconds",
@@ -692,7 +764,7 @@ void run_daemon(TelemPostDaemon *daemon)
exit(EXIT_FAILURE);
}
/* Process inotify event */
if (process_staged_record(record_name, false, daemon)) {
if (process_staged_record(record_name, daemon)) {
unlink(record_name);
}
free(record_name);
@@ -712,18 +784,23 @@ void run_daemon(TelemPostDaemon *daemon)
telem_log(LOG_INFO, "Telemetry post daemon exiting for recycling\n");
break;
}
/* Check if this is a retry */
if (retry_attempt < MAX_RETRY_ATTEMPTS) {
/* Check if this was a retry attempt */
if (retry_attempt > 0) {
/* Stop attempting retries if successful, increment counter if not */
if (staging_records_loop(daemon) == 0) {
retry_attempt = MAX_RETRY_ATTEMPTS + 1;
retry_attempt = 0;
} else {
retry_attempt++;
}
} else if (retry_attempt == MAX_RETRY_ATTEMPTS) {
retry_attempt = MAX_RETRY_ATTEMPTS + 1;
telem_log(LOG_ERR, "Record deliver failed after %d attempts",
MAX_RETRY_ATTEMPTS);
/* Give up if counter reaches MAX_RETRY_ATTEMPTS */
if (retry_attempt == MAX_RETRY_ATTEMPTS) {
telem_log(LOG_ERR, "Record deliver failed after %d attempts",
MAX_RETRY_ATTEMPTS);
retry_attempt = 0;
}
}
/* Check spool */
if (difftime(now, last_spool_run_time) >= spool_process_time) {
spool_records_loop(&(daemon->current_spool_size));
+7 -6
View File
@@ -65,7 +65,7 @@ typedef struct TelemPostDaemon {
*
* @param daemon a pointer to telemetry post daemon
*/
void initialize_daemon(TelemPostDaemon *daemon);
void initialize_post_daemon(TelemPostDaemon *daemon);
/**
* Starts daemon
@@ -85,11 +85,9 @@ void close_daemon(TelemPostDaemon *daemon);
* Processed record written on disk
*
* @param filename a pointor to record on disk
* @param is_retry a boolean value that indicates if
* the record has been previously processed.
* @param daemon post to telemetry post daemon
*/
bool process_staged_record(char *filename, bool is_retry, TelemPostDaemon *daemon);
bool process_staged_record(char *filename, TelemPostDaemon *daemon);
/**
* Scans staging directory to process files that were
@@ -105,8 +103,11 @@ int staging_records_loop(TelemPostDaemon *daemon);
*
* @param headers a pointer to an array with keys and values
* @param body a pointer to the payload
* @param cfg_file a pointer to a non-default configuration
* file to be used.
* @return true if successful, false otherwise
*/
bool post_record_http(char *headers[], char *body);
bool post_record_http(char *headers[], char *body, char *cfg_file);
/**
* Pointer to function to isolate backend call during
@@ -115,7 +116,7 @@ bool post_record_http(char *headers[], char *body);
* @param headers pointer to array of keys
* @param body a pinter to payload
* */
extern bool (*post_record_ptr)(char *headers[], char *body);
extern bool (*post_record_ptr)(char *headers[], char *body, char *cfg_file);
/** Helper functions **/
/* rate limit check */
+6 -14
View File
@@ -33,6 +33,7 @@
#include "common.h"
#include "util.h"
#include "log.h"
bool get_header(const char *haystack, const char *needle, char **line)
{
@@ -64,7 +65,7 @@ bool get_header_value(const char *header, char **value)
*value = strdup(sep);
}
return (bool)(value != NULL);
return (bool)(*value != NULL);
}
void *reallocate(void **addr, size_t *allocated, size_t requested)
@@ -100,10 +101,7 @@ long get_directory_size(const char *dir_path)
dir = opendir(dir_path);
if (!dir) {
ret = -errno;
#ifdef DEBUG
fprintf(stderr, "ERR: Error opening spool dir: %s\n",
strerror(errno));
#endif
telem_perror("Error opening spool dir");
return ret;
}
@@ -113,19 +111,15 @@ long get_directory_size(const char *dir_path)
}
ret = asprintf(&file_path, "%s/%s", dir_path, de->d_name);
if (ret < 0) {
#ifdef DEBUG
fprintf(stderr, "CRIT: Cannot allocate memory\n");
#endif
telem_log(LOG_CRIT, "CRIT: Cannot allocate memory\n");
closedir(dir);
return -ENOMEM;
}
ret = lstat(file_path, &buf);
if (ret < 0) {
#ifdef DEBUG
fprintf(stderr, "ERR: Could not stat file %s: %s\n",
telem_log(LOG_ERR, "Could not stat file %s: %s\n",
file_path, strerror(errno));
#endif
} else {
total_size += (buf.st_blocks * 512);
}
@@ -204,9 +198,7 @@ int validate_classification(char *classification)
}
if (slashes != 2) {
#ifdef DEBUG
fprintf(stderr, "ERR: Classification string should have two /s.\n");
#endif
telem_log(LOG_ERR, "Classification string should have two /s.\n");
return 1;
}
+63 -9
View File
@@ -14,8 +14,10 @@
* details.
*/
#include <stdlib.h>
#include <check.h>
#include "configuration.h"
#include "configuration_check.h"
START_TEST(check_read_config_for_invalid_file)
{
@@ -52,21 +54,69 @@ START_TEST(check_read_valid_config)
"X-Telemetry-TID: 6907c830-eed9-4ce9-81ae-76daf8d88f0f");
ck_assert(config.boolValues[CONF_DAEMON_RECYCLING_ENABLED] == true);
free_config_struct(&config);
}
END_TEST
START_TEST(check_read_valid_config_defaults)
START_TEST(check_default_config)
{
char *config_file = TOPSRCDIR "/src/data/example.conf";
configuration config = { { 0 }, { 0 }, { 0 }, false, NULL };
int ret = set_default_config_values(&config);
ck_assert(ret == true);
ck_assert_str_eq(config.strValues[CONF_SERVER_ADDR], DEFAULT_SERVER_ADDR);
ck_assert_str_eq(config.strValues[CONF_SOCKET_PATH], DEFAULT_SOCKET_PATH);
ck_assert_str_eq(config.strValues[CONF_SPOOL_DIR], DEFAULT_SPOOL_DIR);
ck_assert_str_eq(config.strValues[CONF_RATE_LIMIT_STRATEGY], DEFAULT_RATE_LIMIT_STRATEGY);
ck_assert_str_eq(config.strValues[CONF_CAINFO], DEFAULT_CAINFO);
ck_assert_str_eq(config.strValues[CONF_TIDHEADER], DEFAULT_TIDHEADER);
ck_assert_int_eq(config.intValues[CONF_RECORD_EXPIRY], DEFAULT_RECORD_EXPIRY);
ck_assert_int_eq(config.intValues[CONF_SPOOL_MAX_SIZE], DEFAULT_SPOOL_MAX_SIZE);
ck_assert_int_eq(config.intValues[CONF_SPOOL_PROCESS_TIME], DEFAULT_SPOOL_PROCESS_TIME);
ck_assert_int_eq(config.intValues[CONF_RECORD_WINDOW_LENGTH], DEFAULT_RECORD_WINDOW_LENGTH);
ck_assert_int_eq(config.intValues[CONF_BYTE_WINDOW_LENGTH], DEFAULT_BYTE_WINDOW_LENGTH);
ck_assert_int_eq(config.intValues[CONF_RECORD_BURST_LIMIT], DEFAULT_RECORD_BURST_LIMIT);
ck_assert_int_eq(config.intValues[CONF_BYTE_BURST_LIMIT], DEFAULT_BYTE_BURST_LIMIT);
ck_assert(config.boolValues[CONF_RATE_LIMIT_ENABLED] == DEFAULT_RATE_LIMIT_ENABLED);
ck_assert(config.boolValues[CONF_DAEMON_RECYCLING_ENABLED] == DEFAULT_DAEMON_RECYCLING_ENABLED);
ck_assert(config.boolValues[CONF_RECORD_RETENTION_ENABLED] == DEFAULT_RECORD_RETENTION_ENABLED);
ck_assert(config.boolValues[CONF_RECORD_SERVER_DELIVERY_ENABLED] == DEFAULT_RECORD_SERVER_DELIVERY_ENABLED);
free_config_struct(&config);
}
END_TEST
START_TEST(check_layered_config)
{
char *config_file = TOPSRCDIR "/src/data/example.2.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);
ck_assert_str_eq(config.strValues[CONF_SERVER_ADDR], "http://127.0.0.1");
ck_assert_str_eq(config.strValues[CONF_SOCKET_PATH], DEFAULT_SOCKET_PATH);
ck_assert_str_eq(config.strValues[CONF_SPOOL_DIR], DEFAULT_SPOOL_DIR);
ck_assert_str_eq(config.strValues[CONF_RATE_LIMIT_STRATEGY], DEFAULT_RATE_LIMIT_STRATEGY);
ck_assert_str_eq(config.strValues[CONF_CAINFO], "/tmp/cacert.crt");
ck_assert_str_eq(config.strValues[CONF_TIDHEADER], DEFAULT_TIDHEADER);
ck_assert_int_eq(config.intValues[CONF_RECORD_EXPIRY], DEFAULT_RECORD_EXPIRY);
ck_assert_int_eq(config.intValues[CONF_SPOOL_MAX_SIZE], DEFAULT_SPOOL_MAX_SIZE);
ck_assert_int_eq(config.intValues[CONF_SPOOL_PROCESS_TIME], DEFAULT_SPOOL_PROCESS_TIME);
ck_assert_int_eq(config.intValues[CONF_RECORD_WINDOW_LENGTH], DEFAULT_RECORD_WINDOW_LENGTH);
ck_assert_int_eq(config.intValues[CONF_BYTE_WINDOW_LENGTH], DEFAULT_BYTE_WINDOW_LENGTH);
ck_assert_int_eq(config.intValues[CONF_RECORD_BURST_LIMIT], DEFAULT_RECORD_BURST_LIMIT);
ck_assert_int_eq(config.intValues[CONF_BYTE_BURST_LIMIT], DEFAULT_BYTE_BURST_LIMIT);
ck_assert(config.boolValues[CONF_RATE_LIMIT_ENABLED] == DEFAULT_RATE_LIMIT_ENABLED);
ck_assert(config.boolValues[CONF_DAEMON_RECYCLING_ENABLED] == DEFAULT_DAEMON_RECYCLING_ENABLED);
ck_assert(config.boolValues[CONF_RECORD_RETENTION_ENABLED] == DEFAULT_RECORD_RETENTION_ENABLED);
ck_assert(config.boolValues[CONF_RECORD_SERVER_DELIVERY_ENABLED] == DEFAULT_RECORD_SERVER_DELIVERY_ENABLED);
free_config_struct(&config);
}
END_TEST
@@ -80,6 +130,8 @@ START_TEST(check_read_valid_config_record_retention_delivery)
ck_assert(config.boolValues[CONF_RECORD_RETENTION_ENABLED] == true);
ck_assert(config.boolValues[CONF_RECORD_SERVER_DELIVERY_ENABLED] == false);
free_config_struct(&config);
}
END_TEST
@@ -117,7 +169,8 @@ Suite *config_suite(void)
TCase *t = tcase_create("config");
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_defaults);
tcase_add_test(t, check_default_config);
tcase_add_test(t, check_layered_config);
tcase_add_test(t, check_read_valid_config_record_retention_delivery);
tcase_add_test(t, check_config_initialised);
@@ -132,6 +185,7 @@ int main(void)
{
Suite *s;
SRunner *sr;
int failed;
s = config_suite();
sr = srunner_create(s);
@@ -142,13 +196,13 @@ int main(void)
srunner_set_tap(sr, "-");
srunner_run_all(sr, CK_SILENT);
// failed = srunner_ntests_failed(sr);
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;
return (failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
}
/* vi: set ts=8 sw=8 sts=4 et tw=80 cino=(0: */
+4 -2
View File
@@ -17,6 +17,7 @@
#include <check.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include "common.h"
#include "journal/journal.h"
@@ -239,6 +240,7 @@ int main(void)
{
Suite *s;
SRunner *sr;
int failed;
s = config_suite();
sr = srunner_create(s);
@@ -249,13 +251,13 @@ int main(void)
srunner_set_tap(sr, "-");
srunner_run_all(sr, CK_SILENT);
// failed = srunner_ntests_failed(sr);
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;
return (failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
}
/* vi: set ts=8 sw=8 sts=4 et tw=80 cino=(0: */
+27 -11
View File
@@ -41,7 +41,7 @@ void create_setup(void)
* with the libcheck API...
*/
ck_assert_msg(ret != -ECONNREFUSED,
"Opt-out enabled. Opt in to run these tests");
"First time opt-in required to run test");
}
START_TEST(record_create_non_null)
@@ -98,12 +98,22 @@ void create_teardown(void)
}
}
START_TEST(is_opt_in)
{
int ret;
ret = tm_is_opted_in();
/* Smoke testing function */
ck_assert_msg(ret == 0 || ret == 1,
"Something wrong with opt-in check");
}
END_TEST
START_TEST(record_create_invalid_class1)
{
int ret;
ret = tm_create_record(&ref, 1, "t/t", 2000);
ck_assert_msg(ret != -ECONNREFUSED,
"Opt-out enabled. Opt in to run this test");
"First time opt-in required to run test");
ck_assert(ret == -EINVAL);
}
END_TEST
@@ -113,7 +123,7 @@ START_TEST(record_create_invalid_class2)
int ret;
ret = tm_create_record(&ref, 1, "t/t/t/t", 2000);
ck_assert_msg(ret != -ECONNREFUSED,
"Opt-out enabled. Opt in to run this test");
"First time opt-in required to run test");
ck_assert(ret == -EINVAL);
}
END_TEST
@@ -126,7 +136,7 @@ START_TEST(record_create_severity_underflow)
// Severity of 0 is too low; raise it to 1, the minimum
ret = tm_create_record(&ref, 0, "a/a/a", 2000);
ck_assert_msg(ret != -ECONNREFUSED,
"Opt-out enabled. Opt in to run this test");
"First time opt-in required to run test");
if (asprintf(&result, "%s: %u\n", TM_SEVERITY_STR, 1) < 0) {
return;
@@ -146,7 +156,7 @@ START_TEST(record_create_severity_overflow)
// Severity of 5 is too high; lower it to 4, the maximum
ret = tm_create_record(&ref, 5, "b/b/b", 2000);
ck_assert_msg(ret != -ECONNREFUSED,
"Opt-out enabled. Opt in to run this test");
"First time opt-in required to run test");
if (asprintf(&result, "%s: %u\n", TM_SEVERITY_STR, 4) < 0) {
return;
@@ -172,7 +182,7 @@ void event_id_setup(void)
return;
}
ck_assert_msg(ret != -ECONNREFUSED,
"Opt-out enabled. Opt in to run this test");
"First time opt-in required to run test");
}
START_TEST(record_set_event_id)
@@ -233,9 +243,8 @@ END_TEST
void event_id_teardown(void)
{
if (ref) {
free(ref);
}
// Free record
create_teardown();
free(original_event_id);
}
@@ -252,6 +261,10 @@ Suite *lib_suite(void)
tcase_add_test(t, record_create_version);
suite_add_tcase(s, t);
t = tcase_create("Opt-in");
tcase_add_test(t, is_opt_in);
suite_add_tcase(s, t);
t = tcase_create("invalid classification");
tcase_add_test(t, record_create_invalid_class1);
tcase_add_test(t, record_create_invalid_class2);
@@ -278,6 +291,7 @@ int main(void)
{
Suite *s;
SRunner *sr;
int failed;
s = lib_suite();
sr = srunner_create(s);
@@ -285,11 +299,13 @@ int main(void)
srunner_set_log(sr, NULL);
srunner_set_tap(sr, "-");
// set CK_NOFORK to attach gdb
// srunner_set_fork_status(sr, CK_NOFORK);
srunner_run_all(sr, CK_SILENT);
failed = srunner_ntests_failed(sr);
srunner_free(sr);
return 0;
return (failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
}
/* vi: set ts=8 sw=8 sts=4 et tw=80 cino=(0: */
-192
View File
@@ -1,192 +0,0 @@
/*
* This program is part of the Clear Linux Project
*
* Copyright 2017 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 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;
char out[OUT_MAX_LEN];
const char *filename = TOPSRCDIR "/tests/nc_b64enc_test_files/foobar";
ck_assert_msg(!nc_b64enc_filename(filename, &out[0], n), "Should quit since buffer smaller than content");
}
END_TEST
START_TEST(check_nc_b64_enc_n_file_empty)
{
size_t n = OUT_MAX_LEN;
char out[OUT_MAX_LEN];
char *result = "\0";
const char *filename = TOPSRCDIR "/tests/nc_b64enc_test_files/empty";
ck_assert_msg(nc_b64enc_filename(filename, &out[0], n), "Error opening file");
ck_assert_str_eq(out, result);
}
END_TEST
START_TEST(check_nc_b64_enc_n_file_fo)
{
size_t n = OUT_MAX_LEN;
char out[OUT_MAX_LEN];
char *result = "Zm8=";
const char *filename = TOPSRCDIR "/tests/nc_b64enc_test_files/fo";
ck_assert_msg(nc_b64enc_filename(filename, &out[0], n), "Error opening file");
ck_assert_str_eq(out, result);
}
END_TEST
START_TEST(check_nc_b64_enc_n_file_foob)
{
size_t n = OUT_MAX_LEN;
char out[OUT_MAX_LEN];
char *result = "Zm9vYg==";
const char *filename = TOPSRCDIR "/tests/nc_b64enc_test_files/foob";
ck_assert_msg(nc_b64enc_filename(filename, &out[0], n), "Error opening file");
ck_assert_str_eq(out, result);
}
END_TEST
START_TEST(check_nc_b64_enc_n_file_foobar)
{
size_t n = OUT_MAX_LEN;
char out[OUT_MAX_LEN];
char *result = "Zm9vYmFyZm9vYmFy";
const char *filename = TOPSRCDIR "/tests/nc_b64enc_test_files/foobar";
ck_assert_msg(nc_b64enc_filename(filename, &out[0], n), "Error opening file");
ck_assert_str_eq(out, result);
}
END_TEST
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=";
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");
ck_assert_str_eq(out, expected_out);
}
END_TEST
START_TEST(check_nc_b64_enc_n_filehandler_foobar)
{
size_t n = OUT_MAX_LEN;
char out[OUT_MAX_LEN];
char *result = "Zm9vYmFyZm9vYmFy";
const char *filename = TOPSRCDIR "/tests/nc_b64enc_test_files/foobar";
FILE *fh = NULL;
fh = fopen(filename, "rb");
if (fh == NULL) {
ck_abort_msg("Unable to open foobar file");
}
ck_assert_msg(nc_b64enc_file(fh, &out[0], n), "Result does not match");
ck_assert_str_eq(out, result);
}
END_TEST
Suite *config_suite(void)
{
// A suite is comprised of test cases, defined below
Suite *s = suite_create("nc_b64enc");
// Individual unit tests are added to "test cases"
TCase *t = tcase_create("nc_b64enc");
tcase_add_test(t, check_nc_b64_no_overflow);
tcase_add_test(t, check_nc_b64_enc_n_file_empty);
tcase_add_test(t, check_nc_b64_enc_n_file_fo);
tcase_add_test(t, check_nc_b64_enc_n_file_foob);
tcase_add_test(t, check_nc_b64_enc_n_file_foobar);
tcase_add_test(t, check_nc_b64_enc_n_file_long_text);
tcase_add_test(t, check_nc_b64_enc_n_filehandler_foobar);
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: */
+10 -9
View File
@@ -27,19 +27,19 @@
TelemPostDaemon tdaemon;
bool dummy_post(char *headers[], char *body)
bool dummy_post(char *headers[], char *body, char *cfg_file)
{
return true;
}
bool (*post_record_ptr)(char *headers[], char *body) = dummy_post;
bool (*post_record_ptr)(char *headers[], char *body, char *cfg_file) = dummy_post;
void setup(void)
{
char *config_file = ABSTOPSRCDIR "/src/data/example.conf";
set_config_file(config_file);
initialize_daemon(&tdaemon);
initialize_post_daemon(&tdaemon);
}
START_TEST(check_daemon_is_initialized)
@@ -62,7 +62,7 @@ START_TEST(check_handle_client_with_no_data)
bool success;
char *filename = ABSTOPSRCDIR "/tests/telempostd/empty_message";
success = process_staged_record(filename, false, &tdaemon);
success = process_staged_record(filename, &tdaemon);
// Return true to remove corrupted record
ck_assert(success == true);
}
@@ -75,7 +75,7 @@ START_TEST(check_handle_client_with_incorrect_data)
bool success;
char *filename = ABSTOPSRCDIR "/tests/telempostd/incorrect_message";
success = process_staged_record(filename, false, &tdaemon);
success = process_staged_record(filename, &tdaemon);
// Return true to remove corrupted record
ck_assert(success == true);
}
@@ -88,7 +88,7 @@ START_TEST(check_process_record_with_correct_size_and_data)
bool success;
char *filename = ABSTOPSRCDIR "/tests/telempostd/correct_message";
success = process_staged_record(filename, false, &tdaemon);
success = process_staged_record(filename, &tdaemon);
ck_assert(success == true);
}
END_TEST
@@ -100,7 +100,7 @@ START_TEST(check_process_record_with_incorrect_headers)
bool success;
char *filename = ABSTOPSRCDIR "/tests/telempostd/incorrect_headers";
success = process_staged_record(filename, false, &tdaemon);
success = process_staged_record(filename, &tdaemon);
// Return true to remove corrupted record
ck_assert(success == true);
}
@@ -421,6 +421,7 @@ int main(void)
{
Suite *s;
SRunner *sr;
int failed;
s = config_suite();
sr = srunner_create(s);
@@ -431,13 +432,13 @@ int main(void)
srunner_set_tap(sr, "-");
srunner_run_all(sr, CK_SILENT);
// failed = srunner_ntests_failed(sr);
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;
return (failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
}
/* vi: set ts=8 sw=8 sts=4 et tw=80 cino=(0: */
+31 -6
View File
@@ -22,6 +22,7 @@
#include <unistd.h>
#include "configuration.h"
#include "configuration_check.h"
#include "telemdaemon.h"
#include "common.h"
@@ -58,7 +59,12 @@ void setup(void)
char *config_file = ABSTOPSRCDIR "/src/data/example.conf";
set_config_file(config_file);
initialize_daemon(&tdaemon);
initialize_probe_daemon(&tdaemon);
}
void teardown(void)
{
free_config_file();
}
START_TEST(check_daemon_is_initialized)
@@ -67,6 +73,8 @@ START_TEST(check_daemon_is_initialized)
ck_assert(tdaemon.nfds == 0);
ck_assert(tdaemon.pollfds == NULL);
teardown();
}
END_TEST
@@ -84,9 +92,9 @@ START_TEST(check_add_del_poll_fd)
int fd = 1;
int short events = 1;
add_pollfd(&tdaemon, fd, events);
add_pollfd(&tdaemon, ++fd, events);
add_pollfd(&tdaemon, ++fd, events);
add_pollfd(&tdaemon, 1, events);
add_pollfd(&tdaemon, 2, events);
add_pollfd(&tdaemon, 3, events);
ck_assert_msg(tdaemon.nfds == 3, "Failed to add pollfd");
fd = get_poll_fd(&tdaemon, 0);
@@ -110,6 +118,10 @@ START_TEST(check_add_del_poll_fd)
ck_assert_msg(tdaemon.nfds == 0, "Failed to delete pollfd");
fd = get_poll_fd(&tdaemon, 0);
ck_assert(fd == -1);
/* Cleaning alloctions */
free(tdaemon.pollfds);
teardown();
}
END_TEST
@@ -133,6 +145,8 @@ START_TEST(check_add_remove_client)
remove_client(&(tdaemon.client_head), cl3);
//ck_assert(tdaemon.client_head.lh_first == NULL);
ck_assert_msg(is_client_list_empty(&(tdaemon.client_head)), "Failed to remove clients\n");
teardown();
}
END_TEST
@@ -171,6 +185,8 @@ START_TEST(check_handle_client_with_no_data)
ck_assert_msg(is_client_list_empty(&(tdaemon.client_head)), "Failed to remove client with no data\n");
ck_assert_msg(tdaemon.nfds == 0, "Failed to remove poll fd for client with n data\n");
close(server_fd);
teardown();
}
END_TEST
@@ -197,6 +213,8 @@ START_TEST(check_handle_client_with_incorrect_data)
ck_assert_msg(is_client_list_empty(&(tdaemon.client_head)), "Failed to remove client with no data\n");
ck_assert_msg(tdaemon.nfds == 0, "Failed to remove poll fd for client with n data\n");
close(server_fd);
teardown();
}
END_TEST
@@ -228,6 +246,8 @@ START_TEST(check_handle_client_with_incorrect_size)
ck_assert_msg(is_client_list_empty(&(tdaemon.client_head)), "Failed to remove client with no data\n");
ck_assert_msg(tdaemon.nfds == 0, "Failed to remove poll fd for client with n data\n");
close(server_fd);
teardown();
}
END_TEST
@@ -332,6 +352,8 @@ START_TEST(check_process_record_with_incorrect_headers)
ck_assert_msg(tdaemon.nfds == 0, "Failed to remove poll fd for client with incorrect headers\n");
close(server_fd);
free(record);
teardown();
}
END_TEST
@@ -361,6 +383,7 @@ int main(void)
{
Suite *s;
SRunner *sr;
int failed;
s = config_suite();
sr = srunner_create(s);
@@ -370,14 +393,16 @@ int main(void)
srunner_set_log(sr, NULL);
srunner_set_tap(sr, "-");
// set CK_NOFORK to attach gdb
// srunner_set_fork_status(sr, CK_NOFORK);
srunner_run_all(sr, CK_SILENT);
// failed = srunner_ntests_failed(sr);
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;
return (failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
}
/* vi: set ts=8 sw=8 sts=4 et tw=80 cino=(0: */
+19 -28
View File
@@ -29,14 +29,17 @@
#include "src/probes/klog_scanner.h"
#include "src/probes/oops_parser.h"
char reason[1024];
nc_string *bt;
nc_string *pl;
static char reason[1024];
static nc_string *pl;
void callback_func(struct oops_log_msg *msg)
{
strncpy(reason, msg->lines[0], strlen(msg->lines[0]) + 1);
size_t len = strlen(msg->lines[0]) + 1;
if (len > sizeof(reason)) {
len = sizeof(reason);
}
memcpy(reason, msg->lines[0], len);
reason[sizeof(reason) - 1] = 0;
pl = parse_payload(msg);
}
@@ -54,6 +57,10 @@ void setup_payload(char *oopsfile)
oops_parser_init(callback_func);
split_buf_by_line(buf, buflen);
}
if (buf) {
free(buf);
}
}
// Tests for checking backtrace
@@ -246,6 +253,8 @@ START_TEST(sysctl1_payload)
ck_assert(strstr(pl->str, "#24 sys_ioctl"));
ck_assert(strstr(pl->str, "#25 sysenter_past_esp"));
ck_assert(strstr(pl->str, "#26 quirk_ali7101_acpi"));
nc_string_free(pl);
}
END_TEST
@@ -676,27 +685,6 @@ Suite *config_suite(void)
tcase_add_test(t, bug_kernel_handle_payload);
tcase_add_test(t, bug_kernel_handle_payload_new_format);
//TODO fix
//tcase_add_test(t, badness_payload);
/*
tcase_add_test(t, watchdog_oops);
tcase_add_test(t, warning_oops);
tcase_add_test(t, warn_on_oops);
tcase_add_test(t, two_warnings_oops);
tcase_add_test(t, sysctl1_oops);
tcase_add_test(t, sysctl2_oops);
tcase_add_test(t, softlockup_oops);
tcase_add_test(t, rtnl_oops);
tcase_add_test(t, kernel_null_pointer_oops);
tcase_add_test(t, kernel_bug_oops);
tcase_add_test(t, irq_oops);
tcase_add_test(t, general_protection_fault_oops);
tcase_add_test(t, double_fault_oops);
tcase_add_test(t, bad_page_map_oops);
tcase_add_test(t, bug_kernel_handle_oops);
tcase_add_test(t, badness_oops);
*/
suite_add_tcase(s, t);
return s;
@@ -706,6 +694,7 @@ int main(void)
{
Suite *s;
SRunner *sr;
int failed;
s = config_suite();
sr = srunner_create(s);
@@ -717,14 +706,16 @@ int main(void)
srunner_set_log(sr, NULL);
srunner_set_tap(sr, "-");
// set CK_NOFORK to attach gdb
// srunner_set_fork_status(sr, CK_NOFORK);
srunner_run_all(sr, CK_SILENT);
// failed = srunner_ntests_failed(sr);
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;
return (failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
}
/* vi: set ts=8 sw=8 sts=4 et tw=80 cino=(0: */
+24
View File
@@ -0,0 +1,24 @@
/*
* This program is part of the Clear Linux Project
*
* Copyright 2020 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.
*/
#pragma once
/* Function used only for unit testing */
/* Frees configuration structure */
void free_config_struct(struct configuration *config);
/* Frees configuration file name string */
void free_config_file(void);
+37 -21
View File
@@ -1,4 +1,6 @@
EXTRA_DIST += %D%/taplib.sh
EXTRA_DIST += \
%D%/taplib.sh \
%D%/telemetrics-client.supp
TEST_EXTENSIONS = .sh
@@ -15,7 +17,6 @@ check_PROGRAMS = \
%D%/check_probd \
%D%/check_postd \
%D%/check_probes \
%D%/check_ncb64 \
%D%/check_journal \
%D%/check_libtelemetry
@@ -23,6 +24,7 @@ dist_check_SCRIPTS = \
%D%/create-core.sh
%C%_check_config_SOURCES = \
%D%/configuration_check.h \
%D%/check_config.c
%C%_check_config_CFLAGS = \
@@ -33,7 +35,15 @@ dist_check_SCRIPTS = \
@CHECK_LIBS@ \
$(top_builddir)/src/libtelem-shared.la
if HAVE_SYSTEMD_JOURNAL
if LOG_SYSTEMD
%C%_check_config_CFLAGS += $(SYSTEMD_JOURNAL_CFLAGS)
%C%_check_config_LDADD += $(SYSTEMD_JOURNAL_LIBS)
endif
endif
%C%_check_probd_SOURCES = \
%D%/configuration_check.h \
%D%/check_probd.c \
src/telemdaemon.c \
src/telemdaemon.h \
@@ -83,6 +93,7 @@ EXTRA_DIST += \
%C%_check_postd_LDADD = \
@CHECK_LIBS@ \
@CURL_LIBS@ \
@JSON_C_LIBS@ \
$(top_builddir)/src/libtelem-shared.la
if LOG_SYSTEMD
@@ -148,25 +159,6 @@ if HAVE_SYSTEMD_JOURNAL
endif
endif
%C%_check_ncb64_SOURCES = \
%D%/check_ncb64.c \
src/nica/b64enc.h \
src/nica/b64enc.c
%C%_check_ncb64_CFLAGS = \
$(AM_CFLAGS) \
@CHECK_CFLAGS@
%C%_check_ncb64_LDADD = \
@CHECK_LIBS@
EXTRA_DIST += \
%D%/nc_b64enc_test_files/empty \
%D%/nc_b64enc_test_files/fo \
%D%/nc_b64enc_test_files/foob \
%D%/nc_b64enc_test_files/foobar \
%D%/nc_b64enc_test_files/long_text
%C%_check_journal_SOURCES = \
%D%/check_journal.c \
src/journal/journal.c \
@@ -180,6 +172,13 @@ EXTRA_DIST += \
%C%_check_journal_LDADD = \
@CHECK_LIBS@
if HAVE_SYSTEMD_JOURNAL
if LOG_SYSTEMD
%C%_check_journal_CFLAGS += $(SYSTEMD_JOURNAL_CFLAGS)
%C%_check_journal_LDADD += $(SYSTEMD_JOURNAL_LIBS)
endif
endif
%C%_check_libtelemetry_SOURCES = \
%D%/check_libtelemetry.c \
src/configuration.h \
@@ -195,4 +194,21 @@ EXTRA_DIST += \
$(top_builddir)/src/libtelemetry.la \
$(top_builddir)/src/libtelem-shared.la
if HAVE_SYSTEMD_JOURNAL
if LOG_SYSTEMD
%C%_check_libtelemetry_CFLAGS += $(SYSTEMD_JOURNAL_CFLAGS)
%C%_check_libtelemetry_LDADD += $(SYSTEMD_JOURNAL_LIBS)
endif
endif
@VALGRIND_CHECK_RULES@
VALGRIND_SUPPRESSIONS_FILES = %D%/telemetrics-client.supp
VALGRIND_FLAGS = \
--error-exitcode=1 \
--track-origins=yes \
--leak-resolution=low \
--verbose \
--leak-check=full \
--show-possibly-lost=no
# vim: filetype=automake tabstop=8 shiftwidth=8 noexpandtab
+17 -9
View File
@@ -1,3 +1,19 @@
/*
* This program is part of the Clear Linux Project
*
* Copyright 2015 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 <stdlib.h>
#include <stdio.h>
#include <errno.h>
@@ -36,26 +52,18 @@ char *readfile(char *filepath)
if (fread(bufp, sizeof(char), buflen, fp) != buflen) {
telem_log(LOG_ERR, "Error reading file\n");
} else {
bufp[++buflen] = '\0';
bufp[buflen] = '\0';
}
}
if (fclose(fp) != 0) {
printf("Error closing file: %s\n", strerror(errno));
}
//free(bufp);
} else {
telem_log(LOG_ERR, "Wrong file pathname: %s", filepath);
}
return bufp;
}
/*
char *getbuf()
{
return bufp;
}
*/
unsigned long getbuflen()
{
+38
View File
@@ -0,0 +1,38 @@
# This program is part of the Clear Linux Project
#
# Copyright 2020 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.
# False positive when check is run with CK_FORK=yes
{
ck_fork_yes_false_positive
Memcheck:Leak
fun:malloc
fun:UnknownInlinedFun
fun:UnknownInlinedFun
fun:tag_string_to_list
fun:UnknownInlinedFun
fun:srunner_run_tagged
fun:main
}
# False positive returned by call to shell script from
# with in test.
{
ck_shell_call
Memcheck:Leak
fun:malloc
fun:xmalloc
fun:set_default_locale
fun:main
}