Compare commits

..
265 Commits
Author SHA1 Message Date
Alex Jaramillo 4d32436b6c configure.ac: bump version
Includes static analysis fixes.

Signed-off-by: Alex Jaramillo <alex.v.jaramillo@intel.com>
2025-04-01 10:42:58 -07:00
Alex Jaramillo 3a73ca4e75 Insure body is null terminated
Signed-off-by: Alex Jaramillo <alex.v.jaramillo@intel.com>
2025-04-01 10:02:15 -07:00
Alex Jaramillo b506773879 Add missing free
Signed-off-by: Alex Jaramillo <alex.v.jaramillo@intel.com>
2025-04-01 10:02:15 -07:00
Alex Jaramillo 3b3f882c3a Include null terminator from str
memcpy should copy the null terminator too. The code as it is works
because the destination has null terminators, but we should not relly on
those. We should copy the str and it's null termination.

Signed-off-by: Alex Jaramillo <alex.v.jaramillo@intel.com>
2025-03-31 21:07:42 -07:00
Alex Jaramillo 4f97b51f10 Handle curl_easy_setopt return codes
Signed-off-by: Alex Jaramillo <alex.v.jaramillo@intel.com>
2025-03-31 21:07:42 -07:00
Alex Jaramillo f3e29b5e5a TOCTOU mitigation
Signed-off-by: Alex Jaramillo <alex.v.jaramillo@intel.com>
2025-03-31 21:07:42 -07:00
Alex Jaramillo 5820a1b959 Fix potential resource leak
Signed-off-by: Alex Jaramillo <alex.v.jaramillo@intel.com>
2025-03-31 21:07:42 -07:00
Alex Jaramillo 44d423a7c4 Use correct license file name in make dist
Signed-off-by: Alex Jaramillo <alex.v.jaramillo@intel.com>
2025-03-27 10:11:22 -07:00
Alex Jaramillo 1bcaf35a6c Make the license detectable
This change updates the project's license file to ensure it is detectable
by GitHub, enhancing its visibility on the project landing page.

Signed-off-by: Alex Jaramillo <alex.v.jaramillo@intel.com>
2025-03-27 09:35:45 -07:00
Alex Jaramillo 00556dde05 Update github action
- Use latest version of checkout action.
- Checkout the head of the PR commit.
- Install json-c dependency.
- Use libcheck from OS.

Signed-off-by: Alex Jaramillo <alex.v.jaramillo@intel.com>
2025-03-27 09:15:55 -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
Alex Jaramillo 191d91e8e3 Release v2.1.0 2019-02-20 18:09:47 +00:00
California Sullivan 4b777f71f7 journalprobe: don't process entire journal on restart
This adds an optional argument to journalprobe that allows seeking to
the end of the journal. This is then used in a new service file that is
called by telemctl on restart. This allows us to scan the entire journal
on boot, and only scan new journal entries when restarting, or opting
into telemetry manually. This reduces spam from repeated messages and
fixes a possible privacy concern, as previously running telemctl start
could include journal errors that occurred before telemetrics was opted
into.

Signed-off-by: California Sullivan <california.l.sullivan@intel.com>
2019-02-18 09:09:39 -08:00
Juro Bystricky e27051d1a2 heartbeat: modified payload
Also report locale and uptime.

Signed-off-by: Juro Bystricky <juro.bystricky@intel.com>
2019-02-18 08:59:05 -08:00
Juro Bystricky b7cdf9255d hello.c: additional payload content
Optionally add locale, uptime and list of installed bundles
to the payload. The default behavior is as before.
Additional payload is can be enabled via cmd line arguments:

-l : add locale info
-b : add installed bundles info
-u : add uptime info

For consistency, always use telem_log(LOG_ERR,...) instead
of fprintf(stderr,...).

Signed-off-by: Juro Bystricky <juro.bystricky@intel.com>
2019-02-07 17:11:51 -08:00
ajch 35b5d98c66 Security disclosures link
Added required link for security disclosures.
2019-01-28 11:37:53 -08:00
Patrick McCarty 29917fb464 Add missing entries to gitignore
Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2019-01-28 09:01:59 -08:00
Patrick McCarty 264f53dd47 crashprobe: always remove core files, except when passing --core-file
In modern times, the default core_pattern for Clear Linux OS is set to

    |/usr/lib/systemd/coredump-wrapper %E %P %u %g %s %t %c %h %e

This wrapper script sends core files to both systemd-coredump and
crashprobe (if installed).

Because systemd-coredump will store core files in the systemd journal,
there is no longer a strong reason to preserve the crashprobe core files
in /tmp, aside from when crashprobe's --core-file option is used. If
developers wish to access core files, they can use `coredumpctl` instead
to extract them from the systemd journal.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2019-01-28 09:01:59 -08:00
Juro Bystricky 0705f89533 bert_probe.c: don't return EXIT_SUCCESS in case of a failure
The original code "allocate_payload_buffer" returned 0 if "malloc" failed
and 1 if "malloc" succeeded. The "bertprobe" used this return value upon
exit. However "bertprobe" return code 0 corresponds to EXIT_SUCCESS
and return code 1 corresponds to EXIT_FALURE.

Similarily, "nc_b64enc_filename" returns 1 upon success and 0 upon
failure, so if the function fails, "bertprobe" exits with EXIT_SUCCESS.

To fix this, simplify the code and return with -ENOMEM immediately if memory
allocation fails and explicitly set return value to EXIT_FAILURE if
"nc_b64enc_filename" returns 0.

While at it, rename the label "fail" to "done" as this is common cleanup
exit code for both EXIT_SUCCESS and EXIT_FAILURE.

Signed-off-by: Juro Bystricky <juro.bystricky@intel.com>
2019-01-28 08:59:55 -08:00
Juro Bystricky 57db31a2c8 pythonprobe: don't use inotify
Don't wait for new payload via inotify, let systemd path unit
detect any new payloads in /usr/lib/telemetry/python and start
the pythonprobe service.
Also set the sticky bit for the above folder, preventing users to
modify permissions.

Signed-off-by: Juro Bystricky <juro.bystricky@intel.com>
2019-01-23 20:56:21 -08:00
California Sullivan e20e572fad telemprobd,telempostd: greatly reduce timeouts
We don't need to stay resident for a static two hours. Instead, exit
cleanly after five minutes idle. This also involves reducing the default
and maximum values of spool_process_time, as it was previously set to 30
minutes.

telempostd required several changes with timers, whereas telemprobd only
required changing the values of spool_process_time and
TM_DAEMON_EXIT_TIME, and refreshing the timeout when handling a client.

Signed-off-by: California Sullivan <california.l.sullivan@intel.com>
2019-01-20 21:21:50 -08:00
Juro Bystricky 6b44aa21c7 klogscanner: merge functionality with oopsprobe
The previous sequence of operations was:
1. klogscanner waits for/monitors kernel buffer for messages.
2. If they come, it creates a "raw" file in /var/cache/telemetry/oops.
3. Once this file is placed into /var/cache/telemetry/oops, it is detected by oopsprobe.
4. oopsprobe parses the "raw" file, creates new payload and and sends it to the backend.

This commit merges the operations:
1. klogscanner waits for/monitors kernel buffer for messages.
2. If they come, parses the klog buffer, creates new payload and and sends it to the backend.

While at it, also declare routines as "static" when possible.
Fixed a few missplaced memory freeing.
Simplify the main loop operations, move allocate/free buffer outside
of the loop.

Return SUCCESS if terminated by the signal SIGTERM. This is a clean
way to stop the service. This prevents spamming the journal (and sending
journal/error telemetry data via journalprobe) each time telemetry is
restarted.

Signed-off-by: Juro Bystricky <juro.bystricky@intel.com>
2019-01-20 21:18:58 -08:00
Juro Bystricky dce6e0ee1d python-probe: Initial commit
The purpose of the python-probe is to monitor a specific folder
(as defined by PYTHON_TELEMETRY_DIR) for any Python generated reports.
If any such file is detected, it is sent to the telemetry backend
for analysis.

The reports are typically tracebacks of unhandled Python exceptions
caused by Python programs residing in /usr/bin. It is the responsibility
of Python to generate the traceback files and place them in
PYTHON_TELEMTRY_DIR. The probe will only detect the presence of the files
and send the content of the files as a payload.

Signed-off-by: Juro Bystricky <juro.bystricky@intel.com>
2019-01-15 16:05:04 -07:00
Juro Bystricky d7c1603d12 tm_set_config_file: return status and call from all probes
This change was on a TODO list for a few years.
The function tm_set_config_file could silently fail as it did not
return any status. This forced implementing of all error checks by
the callers of this function.
The current implementation does its own error checks and string copying.
The function returns an error code if errors were encountered.
Since this is a library function, all callers needed to be modified as well.
However, the modifications are code simplifications so this is not too painful.
In addition, the change also affected library documentation as well (void -> int).

As a direct consequence, this change allows simple implementation of the command
line argument "--config_file" uniformly across all probes.

Signed-off-by: Juro Bystricky <juro.bystricky@intel.com>
2019-01-09 11:55:39 -07:00
Alex Jaramillo bb6a08378c Fix for missing restart units
This change fixes a bug where the oops probe and journal probe are not
restarted when telemctl restart is executed.

Signed-off-by: Alex Jaramillo <alex.v.jaramillo@intel.com>
2019-01-04 13:47:06 -07:00
Juro Bystricky 196721b0a1 hello.c: various tweaks
1. Use stderr to print error messages
2. Rename the label 'fail' to 'done' as even non-failing code falls through the label
3. Consistently use EXIT_FAILURE/EXIT_SUCCESS, code sometimes used 1/0 instead.
4. free memory allocated via asprintf
5. Declare print_usage as 'static'

Signed-off-by: Juro Bystricky <juro.bystricky@intel.com>
2018-12-21 11:51:04 -07:00
Juro Bystricky fbc52d01bf bert_probe.c: remove unsupported cmdline option
There is no entry for 'H' command line option, yet the
getopt_long accepts it as a valid option.

Signed-off-by: Juro Bystricky <juro.bystricky@intel.com>
2018-12-21 11:17:18 -07:00
Juro Bystricky 452c4d6ef5 Update docs/README.md
We switched from .md files to .rst files a while ago in v1.11.1.
Since then we genarate man pages using 'rst2man.py', hence the README.md
file needs to be modified to reflect this change.
(/usr/bin/ronn is actually not even availabel in CLR anymore)

Signed-off-by: Juro Bystricky <juro.bystricky@intel.com>
2018-12-21 11:16:53 -07:00
Juro Bystricky 5488d4f75a man pages: update
The .rst were updated. All man pages need to be rebuilt/updated as well.
(Mostly removing/replacing "telemd" references)

Signed-off-by: Juro Bystricky <juro.bystricky@intel.com>
2018-12-19 14:33:24 -07:00
ajch 30fe6b9de1 Adding build status badge from travis 2018-12-19 09:25:59 -08:00
Juro Bystricky a7db38d500 Remove redundant definition MAX_PAYLOAD_SIZE
The file common.h contained two definitions to specify the
maximum allowable payload:

define MAX_PAYLOAD_SIZE 8192
define MAX_PAYLOAD_LENGTH 8192

This patch removes the definition for MAX_PAYLOAD_SIZE and replaces all
occurences of MAX_PAYLOAD_SIZE with MAX_PAYLOAD_LENGTH.

Signed-off-by: Juro Bystricky <juro.bystricky@intel.com>
2018-12-19 10:20:37 -07:00
California Sullivan 4524205238 probes/telem_record_gen.c: fix possible segfault in print_record
When opt-out is enabled, the instanciate_record() function will return
-111 and the headers will not be allocated. Since print_record only
checks that instanciate_record does not return -1 before trying to go
through the headers, a segfault will occur.

This can be seen via:
$ sudo touch /etc/telemetrics/opt-out
$ telem-record-gen -c a/b/c -o -n -p 'payload goes here'

Signed-off-by: California Sullivan <california.l.sullivan@intel.com>
2018-11-30 14:33:01 -08:00
Beth Dean a6ab9fab7e Docs: update .rst files to remove telemd references 2018-11-15 09:03:40 -08:00
Alex Jaramillo d8d3cdee49 Release v2.0.1
Signed-off-by: Alex Jaramillo <alex.v.jaramillo@intel.com>
2018-09-25 15:57:03 +00:00
Alex Jaramillo 839ee5c126 Retry logic for post failure on boot
This change alters the logic of post telemetry message to backend to
handle cases when the network or name resolution service is not
available when the daemon starts. Currently if a message is not
delivered it will be spooled and it should have to wait 900 seconds
(default value) for a future loop to process this message. With this
change when the daemon is started and is unable to deliver telemetry
messages it will atempt to re-send those messages using a t*t delay in
seconds up to t = 7 (or 1, 4, 9, 16, 25, 36, 49 seconds).

Signed-off-by: Alex Jaramillo <alex.v.jaramillo@intel.com>
2018-09-24 15:18:12 -07:00
Alex Jaramillo a08a89ab7f Fix send record check bug
This change fixes a bug where the telem-record-gen probe returns 1
though the execution of the probe was successful.

Signed-off-by: Alex Jaramillo <alex.v.jaramillo@intel.com>
2018-09-06 13:33:39 -07:00
Alex Jaramillo f81e4c7fba Telemetry refactoring
This change adds a daemon that handles telemetry record retention,
record reporting, and record limiting policies. This new daemon
receives records in a folder that is monitored by inotify.

Signed-off-by: Alex Jaramillo <alex.v.jaramillo@intel.com>
2018-09-06 09:57:01 -07:00
Alex Jaramillo 0d07d50a1f Addressing comments 2018-08-27 10:40:12 -07:00
Alex Jaramillo 3b096674dc Addresing comment 2018-08-27 10:40:12 -07:00
Alex Jaramillo c946c7dae9 Clip new line in model string 2018-08-27 10:40:12 -07:00
Alex Jaramillo 91a5c82bd3 Fix memory leak 2018-08-27 10:40:12 -07:00
Alex Jaramillo c02f74be5b Add print only option 2018-08-27 10:40:12 -07:00
Patrick McCarty 2082befdb7 Revert "Removing crash dump configuration"
Installing a crashprobe sysctl conf file by default is, in my opinion,
the most useful way to enable the crashprobe for Linux distributions.

The fact that Clear Linux does not use it anymore does not necessarily
dictate policy for telemetrics-client itself. My preference would be to
simply exclude the conf file in the Clear Linux telemetrics-client
package, since that is very easy to do with autospec. Also, the new
crash handling implementation in Clear Linux requires a systemd patch
that won't be upstreamed, so this is another good reason to install the
conf by default.

This reverts commit d1425173a9.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2018-07-30 16:20:05 -07:00
Patrick McCarty f2206d1cc8 crash probe: retry processing when errors occur
It is possible that transient errors may be raised when processing the
core file, so speculatively retry for any processing errors that appear
for the first run.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2018-07-27 20:54:49 -07:00
Alex Jaramillo d1425173a9 Removing crash dump configuration
This change removes the crash probe exclusive use of the core dump
reported by the kernel. Other component above telemetry will handle
this to properly report core dumps to telemetry (when available).

Signed-off-by: Alex Jaramillo <alex.v.jaramillo@intel.com>
2018-07-27 15:25:01 -07:00
Alex Jaramillo 98b36d5609 Release v1.16.2
This realease contains changes to add source file references as a
workaround for failing libcheck tests. This changes allows telemetry
client to be built using gcc8. This has to be fixed properly in the
future.

Signed-off-by: Alex Jaramillo <alex.v.jaramillo@intel.com>
2018-07-27 14:19:11 -07:00
Alex Jaramillo e59b75689e Release v1.16.1
This release contains:

* Fix for failing unit tests during build when the system where the
package is being built has a telemetry client running.

* Added documentation to probes.

* Documenting local debugging features.

Signed-off-by: Alex Jaramillo <alex.v.jaramillo@intel.com>
2018-05-14 14:39:30 +00:00
Alex Jaramillo e301dad48e Fix for journal tmp directory
This change fixes a bug where journal unit tests fail if the system
where the code is build has telemetry running.

Signed-off-by: Alex Jaramillo <alex.v.jaramillo@intel.com>
2018-05-14 07:22:39 -07:00
Alex Jaramillo 16d8abae81 Bumping libcheck version to 0.12
Bumping libcheck version to 0.12 this version provides additional assertions.

Signed-off-by: Alex Jaramillo <alex.v.jaramillo@intel.com>
2018-05-14 07:22:39 -07:00
avjarami 4aa1f9f3c4 Adding documentation for probes
Including description of collected metadata and specific information
about what data is collected by default probes that are provided by
telemetry client.

Signed-off-by: avjarami <alex.v.jaramillo@intel.com>
2018-04-11 09:19:41 -07:00
avjarami af35ee3a9d Docs for new features
* Added brief description of new configuration values.
* Documenting new header.
* Describing use of features for local debug.

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

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

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

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

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

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

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

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

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

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

Signed-off-by: avjarami <alex.v.jaramillo@intel.com>
2018-03-14 06:22:59 -07:00
avjarami be98da141d Add items to ignore list
Signed-off-by: avjarami <alex.v.jaramillo@intel.com>
2018-03-14 06:22:59 -07:00
avjarami 55bb762479 Release v1.15.0
This release contains:

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

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

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

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

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

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

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

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

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

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

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

Signed-off-by: avjarami <alex.v.jaramillo@intel.com>
2018-02-28 21:54:46 -08:00
Alex Jaramillo da45642759 Release v1.14.3
This release contains:

* Fixes an issue where if the telemetry client bundle is added after
  installation required directories were not created until the OS is
  restarted. This change makes sure that all the required directories
  are created before the telemd service is started.

* Adds an option to telemctl to indicate if telemd.service is active.

* Removes telemetry client temporary directories on telemetry opt-out.

* Add a base64 encoding routine to the nica library, this library can
  be used by probes to encode binaries for http transport to telemetry
  server.

* Fixes configuration parameter on hprobe.

* Adds a probe to report Boot Error Region Table if this record exists
  for more information about BERT, consult ACPI spec
2018-01-08 18:02:28 +00:00
avjarami ed1c85a481 Fixing config parameter in probe
hprobe had an error in the short forme of the CLI config_file parameter,
this change fixes the error.
2018-01-05 13:33:25 -08:00
avjarami b4ac36f7a6 Addresing review comments 2018-01-04 17:44:59 -08:00
avjarami 2f07fd658c BERT record probe
Telemetry probe to report Boot Error Region Table if such entry exists
in /sys/firmware/acpi/tables/
2018-01-04 17:44:59 -08:00
avjarami 4b10dbec86 Base 64 encoding implementation
Providing a minimal function to encode data in base64 to allow the
use of binary data as printable (http transportable) characters.
2018-01-02 13:44:31 -08:00
avjarami bc3c7b6a4d Improvements to telemctl
* Using variable instead of literal

* Simplifying filter that removes folders listed in configuration file
2017-12-07 16:01:01 -08:00
avjarami 15d32841f0 Remove work dirs on opt-out
This change removes working directories when the user opt-out of
reporting platform telemetry.
2017-12-07 16:01:01 -08:00
avjarami e3178e1624 Adding is-active option to telemctl
This change adds a simple way to determine if telemd.service is active.
2017-12-07 16:01:01 -08:00
avjarami 7de4351f16 Create working directries on start
telemd will not start or function properly if expected directories are
not present (i.e. spool dir), this change calls systemd-tempfiles to
create dirs listed listed in telemetrics-dirs.conf
2017-12-07 16:01:01 -08:00
Patrick McCarty 73adb8981a Fix compiler warning
`readv` is declared in <sys/uio.h>

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2017-10-27 10:26:44 -07:00
Alex Jaramillo 5ead074c89 Release v1.14.2
This release adds a unit file to make sure that telemetry daemon will
restart when swupd performs an update.
2017-10-16 18:42:52 +00:00
Alex Jaramillo 80292b5bf8 Restart on update
Because telemd does not restart when an update is installed the daemon
could get out of sync with probes if a new version of a probe requires a
new version of telemd. This change will trigger a restart when an update
takes place.
2017-10-11 17:44:32 -07:00
Patrick McCarty 1517ff4c18 Fix build issues when -DDEBUG is enabled
The call to "fprint()" is a typo and should be "fprintf()".

Also, the "error->message" string is an artifact from the time when
telemetrics-client used glib. Now it uses libnica, which propogates
errors differently. Simply remove the format specifier for now.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2017-09-29 15:12:03 -07:00
Patrick McCarty 85772cb5c5 Release v1.14.1
This release bumps the record format version to account for the new
headers added in the v1.14.0 release.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2017-09-15 10:54:18 -07:00
Patrick McCarty d75fa6fda2 Link to telemetrics-backend repo from the README
Because the telemetrics-backend repo is now hosted on Github, link to it
from the client README, since it is part of a complete telemetrics
solution.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2017-09-15 10:53:09 -07:00
avjarami 6c7c5be2e2 Bumping record format version to 3 2017-09-15 10:52:19 -07:00
Patrick McCarty 913cbb1c1e Release v1.14.0
This release adds three new hardware metadata fields to include in
records created by libtelemetry: board_name, cpu_model, and
bios_version.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2017-09-08 07:32:13 -07:00
Patrick McCarty d3ce2f435c Run uncrustify on the source
Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2017-09-08 07:28:42 -07:00
avjarami d0a3e6bafa Including additional host metadata in headers
Additional headers added: board_name, cpu_model, and bios_version.

    * Board name is a combination of board_name and board_vendor from
    dmi file system.

    * CPU model is read from /proc/cpuinfo.

    * BIOS version is taken from dmi file system.
2017-09-08 07:26:01 -07:00
Patrick McCarty a9b16868f5 Release v1.13.0
This release introduces some changes in behavior for the crash probe and
journal probe:

* The crash probe now ensures core files are kept whenever backtraces
  are scrubbed from telemetry records, or when errors occur during runtime
  operation. This improves the developer experience when running ones own
  programs installed at arbitrary locations on the filesystem and also
  avoids data loss; either the crash backtrace is sent via telemetry, or
  when unable to send the backtrace, the core file is kept.

* The journal probe, by default, now only sends telemetry for journal
  log messages indicating systemd service failures. The previous behavior
  of sending any log messages above a certain log level is now wrapped
  behind the "opt-in-no-privacy-filters" config setting.

Bug fixes include:

* Fixing a segfault crash in the configuration code that occurred
  whenever an incomplete configuration file was encountered.

* Fixing a performance issue in 'telem-record-gen' to avoid extraneous
  strlen() calls in the classification input validation code.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2017-07-05 12:02:21 -07:00
Patrick McCarty cbbc579917 journal probe: conditionally enable log level filters
Because log messages from services contain arbitrary data, and sometimes
this data is privacy sensitive, only enable the log level filters when
the privacy filter override is in effect.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2017-06-30 14:33:31 -07:00
Patrick McCarty 7080839167 configuration: make config errors more actionable
The current behavior of the configuration code is to print a generic
error message and exit if configuration parsing fails.

To clarify the reason for exiting, and thus make the error message more
actionable, print an additional error message that describes which key
from the config is affected and the type of value it accepts.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2017-06-30 14:31:53 -07:00
Patrick McCarty 0d07e76955 configuration: fix regression in parsing that results in segfault
When a key from a INI file is missing, libnica returns NULL, so we need
to properly check for a non-NULL return value before passing it to
strdup().

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2017-06-30 14:31:53 -07:00
Patrick McCarty 1c8af05cb9 telem-record-gen: avoid unnecessary strlen() calls
Since strlen(opt_class) is already called before this for loop, there is
no need to call it again. It also avoids repeatedly calling strlen() as
part of the for-loop conditional check.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2017-06-27 21:02:44 -07:00
Patrick McCarty cb4b951a1a crash probe: use a helper function for string prefix checks
Since the code repeats the same logic in several places, it is
convenient to split the routine into a helper function, startswith().

This also fixes a bug with the earlier port to libnica: in the
in_clr_build() function, strstr() was chosen as the replacement function
instead of strncmp(), which means a match will be found in any part of
the string. However, a match should only be found when it is a prefix.
To fix, switch the strstr() in that function to use startswith() as
well.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2017-06-27 21:02:44 -07:00
Patrick McCarty bb018a158f Document condition under which core files are removed
Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2017-06-26 17:14:50 -07:00
Patrick McCarty 577484cdb2 crash probe: also keep core files when errors occur
During the processing of a core file or sending telemetry, any numbers
of errors may occur. Make sure the core file is not unlinked under these
conditions.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2017-06-26 17:14:50 -07:00
Patrick McCarty 049c9a35b9 crash probe: keep core files when backtraces are scrubbed
When the path filters for privacy are in effect, backtraces are scrubbed
from records, and this results in the core files being unlinked.

However, this is not friendly behavior for the developer. A common
situation that triggers the path filters is installing custom binaries
on the system (say, under /usr/local/bin or /opt/bin) for testing
purposes. To better enable developers to debug their programs, having
the core files available to process with gdb is very valuable.

Eventually, I would like to add an opt-in to keep all core files, but
I'll wait until the configuration code is refactored.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2017-06-26 17:14:50 -07:00
Patrick McCarty 56a6a4777b Release v1.12.4
This release includes a change that results in lower memory consumption
for telemd when the service is idle.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2017-06-23 11:16:07 -07:00
Patrick McCarty d2ecf76c70 Init/deinit libcurl global environment for each POST
When the daemon is sitting idle, we are seeing libcurl consume around
2MB of memory, which had been previously allocated on-the-fly for its
global environment.

To have more control over this memory consumption, making sure the
daemon uses as little memory as possible when doing no work, explicitly
allocate the libcurl global environment before each POST and deallocate
it afterwards.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2017-06-20 19:33:06 -07:00
Patrick McCarty f2aa671513 Release v1.12.3
This release fixes a buffer overrun issue in the klogscanner.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2017-06-20 15:41:00 -07:00
Patrick McCarty a1fcf14c3d Fix max number of bytes read by SYSLOG_ACTION_READ
In case the value returned by SYSLOG_ACTION_SIZE_BUFFER exceeds MAX_BUF,
the buffer later allocated has size MAX_BUF, but SYSLOG_ACTION_READ may
read more than MAX_BUF bytes to store in the buffer, leading to a buffer
overrun.

Fix the issue by ensuring SYSLOG_ACTION_READ reads at most "buflen"
bytes, the size of the allocated buffer.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2017-06-20 12:22:34 -07:00
Patrick McCarty ad8209115f Enforce buffer length cap on first loop iteration
Since loopsize is initialized to 0, the conditional statement where
loopsize increments led to the buffer size check not being executed the
first time through the loop.

Simply remove the counter variable to fix the issue.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2017-06-20 12:22:34 -07:00
Patrick McCarty fdb2fa0463 Print specific error for SYSLOG_ACTION_SIZE_BUFFER query failure
Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2017-06-20 12:22:34 -07:00
Patrick McCarty 54b66b2820 Release v1.12.2
This release completes the remaining work to remove the glib dependency
in favor of libnica.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2017-06-09 14:05:04 -07:00
Patrick McCarty bccd669c91 Remove buildtime checks for glib; update README
Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2017-06-09 14:01:51 -07:00
Patrick McCarty 01fd01a49a pstore probe: switch to use libnica hashmaps
This completes the glib dependency removal work. The next commit will
clean up the makefiles and documentation.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2017-06-09 14:01:51 -07:00
Patrick McCarty c172e5f4e5 Release v1.12.1
This release includes further work on removing the glib dependency in
favor of libnica and updates the README file.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2017-05-26 11:36:52 -07:00
Patrick McCarty 803c8ec5ed Remove debug statement
Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2017-05-26 11:14:19 -07:00
Patrick McCarty 80fd20ba66 Fix distcheck
Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2017-05-26 11:14:19 -07:00
Patrick McCarty 9f153dc45d Fix some style issues missed by uncrustify
Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2017-05-26 11:14:15 -07:00
Patrick McCarty 3edb3d628f Run uncrustify
Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2017-05-26 11:12:46 -07:00
Arjan van de Ven b193344145 Make sure to strdup() the ini file keys 2017-05-26 11:11:55 -07:00
Arjan van de Ven 59c63d8c40 Replace glib ini file parsing with nica ini file parsing 2017-05-26 11:11:55 -07:00
Victor Rodriguez 97768589d3 Improve README
Signed-off-by: Victor Rodriguez <victor.rodriguez.bahena@intel.com>
2017-05-11 15:51:20 -07:00
Patrick McCarty 7d3a9d2834 Release v1.12.0
This release introduces a new feature for the crash probe. It will now
collect source file and line number information for each frame in a
backtrace if the data is available.

Also, this release saw the beginning of migrating the code base to use
libnica instead of glib, with the goal of reducing memory footprint of
the telemetry daemon and probes. Future releases will convert more
existing glib usage to libnica.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2017-05-11 13:19:41 -07:00
Patrick McCarty 0af684e94e Fix bug in is_banned_path(); group conditionals
The checks in is_banned_path() for a path prefix not under /usr needed
to be connected with a logical AND, not OR, as implied by the recent
changes.

Also, use an explicit logical OR for the other two groups of
conditionals so that they are grouped similarly to the checks that
required an AND.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2017-05-11 12:55:33 -07:00
Patrick McCarty 51374db118 Remove more unused glib includes
Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2017-05-11 11:22:42 -07:00
Patrick McCarty dfb40babaa Remove unneeded glib.h include
telem-record-gen no longer depends on glib, so the include can be
removed.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2017-05-11 10:29:18 -07:00
Patrick McCarty b7619e1a1c Remove glib dependency for ASCII string check
As far as I can tell, the standard library doesn't have a function to
check for an ASCII string. Equivalently, we can iterate over each
character in the string, byte by byte, and verify that only 7-bit
characters are present.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2017-05-11 10:29:18 -07:00
Patrick McCarty 3be37cebb5 Use getopt_long() in telem-record-gen
One more binary was using GOptionContext: telem-record-gen. Convert it
to use getopt_long() as well.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2017-05-11 10:29:18 -07:00
Patrick McCarty 15a32b6346 Fix code style issues in journal.c
Run uncrustify again to catch further whitespace issues.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2017-05-11 10:29:18 -07:00
Patrick McCarty e0e2aae341 Use getopt_long() in the crash probe
Continuing the removal of dependency on glib, switch to use
getopt_long() instead of the GOptionContext API for the crash probe.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2017-05-11 10:29:18 -07:00
Arjan van de Ven 4d1f6de3b5 replace the regexps with more simple tests 2017-05-11 09:19:41 -07:00
Patrick McCarty f3a641b161 Use getopt_long() in the journal probe
Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2017-05-10 12:54:43 -07:00
Patrick McCarty 9018362ffb Fix style issues missed by uncrustify
Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2017-05-10 12:39:48 -07:00
Patrick McCarty eedcb88cb7 Run uncrustify
Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2017-05-10 12:07:44 -07:00
Patrick McCarty 2deeef65ad Update path filter for uncrustify
Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2017-05-10 12:07:26 -07:00
Patrick McCarty dab667eeea Fix distcheck
Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2017-05-10 12:00:09 -07:00
Patrick McCarty 787102c4fb Clean up makefile and header references for consistency
Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2017-05-10 11:58:55 -07:00
Arjan van de Ven 12d1c82650 Print file name / line number in backtrace 2017-05-10 11:27:52 -07:00
Arjan van de Ven 46d88d6315 more crash probe glib string removals 2017-05-10 11:27:52 -07:00
Arjan van de Ven 089183c9e0 remove some spurious glib includes 2017-05-10 11:27:52 -07:00
Arjan van de Ven f354078d3d convert the journal probe to getopt 2017-05-10 11:27:52 -07:00
Arjan van de Ven db85aa9a18 stop using g_print 2017-05-10 11:27:52 -07:00
Arjan van de Ven f5899c7ba6 convert more of the crash probe 2017-05-10 11:27:52 -07:00
Arjan van de Ven 657c8792d5 make the pstore probe use nica strings 2017-05-10 11:27:52 -07:00
Arjan van de Ven 365cfeee9a convert the journal probe to nica strings 2017-05-10 11:27:52 -07:00
Arjan van de Ven 4957452f66 convert the crash probe to nica strings 2017-05-10 11:27:52 -07:00
Arjan van de Ven e02e22f192 replace glib string operations with nica string operations 2017-05-10 11:27:52 -07:00
Patrick McCarty 60bbe963cb Fix make check for Travis builds
The libcheck on Ubuntu is too old, so install a newer version to enable
make check.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2017-05-05 12:53:33 -07:00
Patrick McCarty 1ffc3a00c2 configure: add comment regarding minimum supported libcheck
Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2017-05-05 12:51:41 -07:00
Patrick McCarty 60edcdb165 Revert "don't be as picky about check"
This reverts commit aa4f9e3058.

The oldest version of libcheck that supports TAP output is 0.9.12, so we
need to use >= 0.9.12.
2017-05-05 12:51:41 -07:00
Arjan van de Ven bc12e6d5f7 the klog_scanner does not use glib.h 2017-05-05 12:18:42 -07:00
Arjan van de Ven e9d29a4e7b use isspace and isprint directly 2017-05-05 12:18:42 -07:00
Arjan van de Ven 6295f0f4dc a gchar is just a char typedef 2017-05-05 12:18:42 -07:00
Arjan van de Ven 4cf3c7e8ac a guint is just an uint32_t so lets use standard types 2017-05-05 12:18:42 -07:00
Arjan van de Ven d9d957e532 make distcheck is b0rk 2017-05-05 16:51:44 +00:00
Arjan van de Ven f88d646ae1 make check is b0rk 2017-05-05 16:49:39 +00:00
Arjan van de Ven e0619807c9 drop flags the OS should provide 2017-05-05 16:47:01 +00:00
Arjan van de Ven f07c9b168c add libdw-dev 2017-05-05 16:42:50 +00:00
Arjan van de Ven 7333bc7968 add libelf-dev 2017-05-05 16:40:11 +00:00
Arjan van de Ven aa4f9e3058 don't be as picky about check 2017-05-05 16:38:13 +00:00
Arjan van de Ven 6c36253351 add check 2017-05-05 16:35:56 +00:00
Arjan van de Ven e78ca8b66f avoid the stone age 2017-05-05 16:33:18 +00:00
Arjan van de Ven a449e6cec1 add autoconf 2017-05-05 16:30:29 +00:00
Arjan van de Ven 5963f983cd one more different package name 2017-05-05 16:27:21 +00:00
Arjan van de Ven c65e58f696 resolve deps 2017-05-05 16:25:54 +00:00
Arjan van de Ven 1d5b2d0669 add first stab travis file 2017-05-05 16:21:36 +00:00
Patrick McCarty 356be6738f Release v1.11.2
This release includes a couple of fixes for the man pages.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2017-04-06 09:59:36 -07:00
Auke Kok e73870b916 Fix wrong reference to the config file in man pages. 2017-04-06 09:57:50 -07:00
Auke Kok c7e783f2b1 Add FILES section, explain static machine id file. 2017-04-06 09:57:50 -07:00
Patrick McCarty 3bb04aff3f Release v1.11.1
This release includes updates for man pages and improves support for the
new oops format in Linux 4.10 and newer.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2017-03-22 10:49:26 -07:00
Patrick McCarty 26fc7b69bb Fix make distcheck
I forgot to add the new test file to EXTRA_DIST, so add it here.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2017-03-22 10:48:54 -07:00
Auke Kok e3ede3cd88 Refresh the existing man pages' output.
Made with `make manpages` of course.
2017-03-21 22:57:07 -07:00
Auke Kok dd44e91d4a Convert man pages to RST. 2017-03-21 22:57:07 -07:00
Patrick McCarty a403d884bc tests: add test case for new oops format
Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2017-03-21 22:55:03 -07:00
Patrick McCarty e699fd34e2 oops parser: support oops stack traces for Linux 4.10+
Linux 4.10 introduced some format changes for oopses; one of the changes
was removing memory addresses from each stack frame.

This commit adds support for the new format and retains compatibility
for the previous format (4.9 and earlier).

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2017-03-21 22:55:03 -07:00
Patrick McCarty 266039a4e5 oops parser: add/use skip_space; fix skip_spaces
The former skip_spaces() function only skipped a single space, so rename
it to skip_space(), and make skip_spaces() greedily consume spaces.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2017-03-21 22:55:03 -07:00
Patrick McCarty 3d24527096 Release v1.11.0
This release reworks the journal probe to send records for log messages
with higher log levels (LOG_EMERG, LOG_ALERT, LOG_CRIT, LOG_ERR), and
also for messages that match EXIT_CODE="exited", which is set for
systemd services that have exited with an error code.

Also, a new opt-in feature has been added to disable certain privacy
filters in the probes. With the filters disabled, the crash probe will
collect backtraces for any crashing binary on the system, regardless of
where the binary is installed. Also, the oops probe will collect real
register values from kernel oopses instead of using "zero" or
"non-zero".

The opt in feature described above can be enabled by creating a file at:

  /etc/telemetrics/opt-in-no-privacy-filters

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2017-02-17 11:39:33 -08:00
Patrick McCarty 9b435f39e5 journal probe: remove obsolete LOG_DEBUG message
A return value of 0 can also indicate send_data() failure, so remove
this log message.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2017-02-17 11:35:25 -08:00
Patrick McCarty 20c051fe59 Document the logical expression for journal entry matching
Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2017-02-17 11:35:25 -08:00
Patrick McCarty 96c1cd5547 journal probe: filter LOG_EMERG logs as well
I omitted log level 0 from the filter, so add it here.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2017-02-17 11:35:25 -08:00
Patrick McCarty e375fe6825 journal probe: send records for every log message
The previous logic concatenates log messages on initial startup, when
the entire journal is read to process existing messages. But doing so
might run into the payload size limit (8KB), and thus fail to create a
record.

Sending one record per log message will ensure that the payload size
remains relatively small, almost always below the 8KB size limit.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2017-02-17 11:35:25 -08:00
Patrick McCarty 160a4d01bf journal probe: filter on EXIT_CODE as well
This field appears to be set when services fail, so filter on it as
well.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2017-02-17 11:35:25 -08:00
Patrick McCarty 3f7b839cd8 journal probe: use the new macros
Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2017-02-17 11:35:25 -08:00
Patrick McCarty 07595885c4 journal probe: add macros to help with using the journal API
To avoid having to read the repetitive error handling when adding
journal filters, add some helper macros.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2017-02-17 11:35:25 -08:00
Patrick McCarty 625eea615e journal probe: fix formatting of some log messages
Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2017-02-17 11:35:25 -08:00
Patrick McCarty 8a69b22895 journal probe: add newlines for to each message in the payload
The make payloads more legible, make sure separate messages are newline
separated.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2017-02-17 11:35:25 -08:00
Patrick McCarty b4a0989d0e journal probe: match all messages ERR or higher
Previous behavior was to only filter LOG_ERR messages from the
telemetrics crashprobe, but it will be helpful to make this probe more
generic to capture log messages with the highest log levels.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2017-02-17 11:35:25 -08:00
Patrick McCarty 41f53507d1 journal probe: fix error handling of read_new_entries()
The error case is negative, so fix the return code conditional check
appropriately.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2017-02-17 11:35:25 -08:00
Patrick McCarty 3aeb31ee3d Add opt-in feature to disable privacy filters
There are some filters in place that prevent probes from collecting
specific types of data. In particular, the oops probe will report "zero"
or "non-zero" for register values in oopses instead of the actual
values. And the crash probe does not send backtraces if binaries live
outside of /usr or under /usr/local.

This commit keeps these filters in place but offers the capability to
disable the filters by creating a file named

  /etc/telemetrics/opt-in-no-privacy-filters

Any future telemetry privacy filters can check for this file to alter
the reporting level.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2017-02-17 10:56:55 -08:00
Patrick McCarty 4ce8ff9990 Clarify path filtering logic in the crash probe
Since only one path is being checked for a program, and it returns a
boolean, rename the function and call site appropriately.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2017-02-17 10:56:55 -08:00
124 changed files with 10049 additions and 3146 deletions
+29
View File
@@ -0,0 +1,29 @@
name: C/C++ CI
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha }}
- name: install dependencies
run: |
sudo apt-get update
sudo apt-get --fix-missing install libcurl4-gnutls-dev valgrind libelf-dev libdw-dev libjson-c-dev check
- 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
+7 -2
View File
@@ -33,7 +33,9 @@ build-aux/m4/ltversion.m4
*.trs
/cscope.*
/tags
telemd
telemprobd
telempostd
telemctl
autoscan.log
configure.scan
tprobe
@@ -42,9 +44,9 @@ crashprobe
journalprobe
telem-record-gen
klogscanner
oopsprobe
pstoreclean
pstoreprobe
telem_journal
make.out
types_c.taghl
src/recomp.sh
@@ -53,7 +55,10 @@ src/gcc.out
tests/demo
tests/check_config
tests/check_daemon
tests/check_journal
tests/check_libtelemetry
tests/check_postd
tests/check_probd
tests/check_probes
src/data/*.path
src/data/*.service
+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
@@ -62,7 +62,7 @@ Coding Style
- To make type casts stand out more, include no space between the cast and the
variable, etc. to cast:
GString **foo = (GString **)bar;
nc_string **foo = (nc_string **)bar;
- Whenever arithmetic operators are used, always include one space before and
after each operator:
View File
+3 -2
View File
@@ -4,7 +4,7 @@ ACLOCAL_AMFLAGS = -I build-aux/m4
EXTRA_DIST = \
AUTHORS \
LICENSE.LGPL-2.1
LICENSE
DISTCHECK_CONFIGURE_FLAGS = \
--with-systemdsystemunitdir=$$dc_install_base/$(systemdunitdir) \
@@ -15,10 +15,11 @@ 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
release:
-32
View File
@@ -1,32 +0,0 @@
telemetrics-client
==================
This package provides the front end component of a complete telemetrics
solution for Linux-based operating systems. Specifically, the front end
component includes:
- telemetrics probes that collect specific types of data from the operating
system.
- a library, libtelemetry, that telemetrics probes use to create telemetrics
records and send them to the daemon for further processing.
- a daemon, telemd, that prepares the records to send to a telemetrics server
(not included in this source tree), or spools the records on disk in case
it's unable to successfully deliver them.
Build dependencies
==================
- libcheck
- libcurl
- glib
- elfutils, which provides libelf and libdwfl libraries..
- (optional) libsystemd, for syslog-style logging to the systemd journal, and
socket/path activation of telemd by systemd.
+339
View File
@@ -0,0 +1,339 @@
![](https://github.com/clearlinux/telemetrics-client/workflows/C%2FC++%20CI/badge.svg)
Telemetrics-client
==================
This package provides the front end component of a complete telemetrics
solution for Linux-based operating systems. Specifically, the front end
component includes:
- telemetrics probes that collect specific types of data from the operating
system. For more info on probes go [here](./src/probes).
- a library, libtelemetry, that telemetrics probes use to create telemetrics
records and send them to the daemon for further processing.
- telemprobd, a daemon that handles communication with probes and forwards records
to a second daemon.
- telempostd, this daemon sends telemetry records to a telemetrics server
(not included in this source tree), or spools the records on disk in case
it's unable to successfully deliver them.
A telemetrics server implementation that works with this component is available
from
[clearlinux/telemetrics-backend](https://github.com/clearlinux/telemetrics-backend).
Build dependencies
---------------------
- libcheck
- libcurl
- elfutils, which provides libelf and libdwfl libraries..
- (optional) libsystemd, for syslog-style logging to the systemd journal, and
socket/path activation of telemprobd and telempostd by systemd.
Build and installation
---------------------
```{r, engine='bash', count_lines}
$ ./autogen.sh
$ ./configure
$ make
```
Set up
---------------------
There is a config file installed at
```{r, engine='bash', count_lines}
/usr/share/defaults/telemetrics/telemetrics.conf
```
To make modifications, copy that file to
```{r, engine='bash', count_lines}
/etc/telemetrics/telemetrics.conf
```
and modify the /etc version.
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. 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. To check the status of
telemetrics-client use:
```{r, engine='bash', count_lines}
telemctl is-active
telemprobd : active
telempostd : active
```
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:
```{r, engine='bash', count_lines}
systemctl enable telemprobd.socket telempostd.path
```
Method 2:
Enable services, which automatically enables the socket and path
units as well:
```{r, engine='bash', count_lines}
systemctl enable telemprobd.service
systemctl enable telempostd.service
```
Usage
---------------------
Once the client is running, the telemetrics probes will be ready to use.
Available probes:
* hprobe: A test program that utilizes libtelemetry to ensure
that telemetrics-client works. It sends a "hello world" record to the server.
* crash probe: A handler for core files that sends the corresponding backtraces
to the server.
The client uses the following configuration options from the configuration file:
* server: This specifies the web server that the telempostd sends the telemetry records to
* socket_path: This specifies the path of the unix domain socket that the
telemprobd listens on for connections from the probes
* spool_dir: This config option is related to spooling. If the daemon is not
able to send the telemetry records to the backend server due to reasons such
as the network availability, then it stores the records in a spool directory.
This option specifies that path of the spool directory. This directory should
be owned by the same user that the daemon is running as.
```{r, engine='bash', count_lines}
mkdir -p /var/spool/telemetry
chown -R telemetry:telemetry /var/spool/telemetry
systemctl restart telemprobd.service
```
* record_expiry: This is the time in minutes after which the records in the
spool directory are deleted by the daemon.
* spool_max_size: This specifies the maximum size of the spool directory. When
the size of the spool directory reaches this limit, new telemetry records are
dropped by the daemon.
* spool_process_time: This specifies the time interval in seconds that the
daemon waits for before checking the spool directory for records. The daemon
picks up the records in the order of modification date and tries to send the
record to the server. It sends a maximum of 10 records at a time. If it was
able to send a record successfully, it deletes the record from the spool. If
the daemon finds a record older than the "record_expiry" time, then it
deletes that record. The daemon looks at a maximum of 20 records in a single
spool run loop.
* rate_limit_enabled: This determines whether rate-limiting is enabled or
disabled. When enabled, there is a threshold on both records sent within a
window of time, and record bytes sent within a window a time.
* record_burst_limit: This is the maximum amount of records allowed to be
passed by the daemon within the record_window_length of time. If set to -1,
the rate-limiting for record bursts is disabled.
* record_window_length: This is the time, in minutes (0-59), that establishes
the window length for the record_burst_limit. EX: if record_burst_window=1000
and record_window_length=15, then no more than 1000 records can be passed
within any given fifteen minute window.
* byte_burst_limit: This is the maximum amount of bytes that can be passed by
the daemon within the byte_window_length of time. If set to -1, the
rate-limiting for byte bursts is disabled.
* byte_window_length: This is the time, in minutes (0-59), that establishes the
window length for the byte_burst_limit.
* rate_limit_strategy: This is the strategy chosen once the rate-limiting
threshold has been reached. Currently the options are 'drop' or 'spool', with
spool being the default. If spool is chosen, records will be spooled and sent
at a later time.
* record_retention_enabled: When this key is enabled (true) the daemon saves a
copy of the payload on disk from all valid records. To avoid the excessive use
of disk space only the latest 100 records are kept. The default value for this
configuration key is false.
* record_server_delivery_enabled: This key controls the delivery of records to
```server```, when enabled (default value) the record will be posted to the
address in the configuration file. If this configuration key is disabled (false)
records will not be spooled or posted to backend. This configuration key can
be used in combination with ```record_retention_enabled``` to keep copies of
telemetry records locally only.
Data reported
---------------------
The data reported by the telemetry client could be understood as two main sets:
metadata and a payload.
The metadata is used to report details of a machine's architecture. The
following are the metadata values currently collected (Record Format Version 4):
* record_format_version: version of the record, currently is 'Version 4'. This
value changes when new metadata is added.
* classification: this field is used to identify the type of record sent by a
specific client probe; classifications use the format DOMAIN/PROBE/REST, where
DOMAIN is the vendor of the probe, PROBE is the probe name, and REST is a
probe-defined field to classify what is contained in the payload.
* severity: this is an integer value between 1 and 4 where 1 is "low" and 4 is
"critical"
* machine_id: a machine identifier that is rotate every 3 days for privacy reasons.
* creation_timestamp: timestamp when the record was collected.
* arch: a string describing machine architecture i.e. 'x86_64'.
* host_type: a string with the combination of 'System Vendor', 'Product Name', and
'Product Version' read from dmi file system.
* build: OS build number.
* kernel_version: Kernel version.
* payload_format_version: version of the payload, currently is 'Version 1'.
* system_name: the value after 'ID=' from '/etc/os-release' (or distribution
provided folder)
* board_name: a string read from dmi file system that combines 'Board Name' and
'Board Vendor'.
* cpu_model: cpu model name extracted from '/proc/cpuinfo'.
* bios_version: BIOS version.
* event_id: an id to group multiple records if these were generated by a single
event occurrence.
The payload as mentioned above is reported by probes. The telemetry library adds
the metadata to the payload (done programatically when using library API) for
more information about probes go [here](./src/probes).
Machine id
---------------------
The machine id reported by the telemetry client is rotated every 3 days for
privacy reasons. If you wish to have a static machine id for testing purposes,
you can opt in by creating a static machine id file named
"opt-in-static-machine-id" under the directory "/etc/telemetrics/". Where
"unique machine ID" is your desired static machine ID:
```{r, engine='bash', count_lines}
# mkdir -p /etc/telemetrics
# echo "unique machine ID" > /etc/telemetrics/opt-in-static-machine-id
```
The telemetry client reads, at most, the first 32 characters from this file
uses it for the machine id. You can put a string like 'my-machine-name' in this
file to easily identify your machine. Restart telemprobd for the machine id
changes to take effect by running:
```{r, engine='bash', count_lines}
# systemctl restart telemprobd.service
```
You can switch back to the rotating machine id by deleting the override file
and restarting the client. You can do a quick test to check that your
machine-id has changed by running "hprobe" and verifying that a record has
landed on your backend telemetrics server, with the specified machine id.
Event Id
----------------------
This is a 32 character lowercase hexadecimal string i.e. '5de9de8d5f3c6a7d445d75ba01cc3322'.
This header is used to group multiple records by an event id. Before this header
every single record could have been thought of an event, however this is not always
the case. There are "events" that trigger the creation of multiple records (i.e.
updates). The ```event_id``` header was added for probes with the capability to
detect events and group records based on such events. This header was added to
```telem-record-gen``` and can be specified using the ```-e``` (```--event-id```
long form) switch.
```{r, engine=bash, count_lines}
-e, --event-id Event id to use in the record
```
Debugging locally with telemetrics-client
-----------------------------------------------
The function of the telemetrics-client is to handle the transport of information
reported by a probe to a backend (see ```server``` key in configuration). This
information is helpful for developers to debug and fix reported crashes, however
developers not always have access to the backend in these case users can leverage
features added for local debugging. The following is a list of steps to enable
local debug:
* *Enabling record retention*: this step configures telempostd to keep
copies of telemetry records locally. To enable record retention set the value of
```record_retention_enabled``` from ```false``` to ```true```. Optionally set
```record_server_delivery_enabled`` to ```false``` to keep records local only.
Remember to restart the client after configuration values are updated
(```telemctl restart```).
* *Creating a record*: run ```hprobe``` command to create a record for the purposes
of this step by step guide. Once we have the record or records that you need to
capture locally you can display the data.
* *Displaying record metadata*: telempostd keeps metadata of any valid record,
to display this data a new option to telemctl was added ```telemctl journal```.
Assuming that the last record created was the record from previous step `hprobe` we
can use `tail -n 1` to print the last created record only, i.e.
```
$ sudo telemctl journal | tail -n 1
$ org.clearlinux/hello/world Mon 2018-04-02 17:48:01 UTC a19a0d41ba16788881e274b19b8a1be4 5de9de8d5f3c6a7d445d75ba01cc3322 60c014cd-4693-40f1-b334-548cd932949b
```
The headers for the metadata (along with other information) can be printed using the
```-V``` switch with ```telemctl journal``` command, i.e.
```
$ sudo telemctl journal -V | head -n 1
$ Classification Time stamp Record ID Event ID Boot ID
```
* *Displaying record payload*: to print the content of a record payload you can use
the ```-i``` (```--include_record``` long format) option to ```telemctl journal```
command. To print the specific record you created you can use the option ```-r```
(```--record_id``` long format) with the Record Id of the generated record, i.e.
```
$ sudo telemctl journal --record_id a19a0d41ba16788881e274b19b8a1be4 --include_record
$ org.clearlinux/hello/world Mon 2018-04-02 17:48:01 UTC a19a0d41ba16788881e274b19b8a1be4 5de9de8d5f3c6a7d445d75ba01cc3322 60c014cd-4693-40f1-b334-548cd932949b
$ hello
```
## Security Disclosures
To report a security issue or receive security advisories please follow procedures
in this [link](https://01.org/security).
-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 -5
View File
@@ -2,7 +2,6 @@ AM_CFLAGS = \
-std=gnu99 \
-pedantic \
-Wall \
-fstack-protector \
-Wformat \
-Wformat-security \
-Wimplicit-function-declaration \
@@ -12,9 +11,11 @@ AM_CFLAGS = \
-Wconversion \
-Wunreachable-code \
-funsigned-char \
-fstack-protector-strong \
-fPIE \
-fPIC
-fPIC \
-fno-strict-overflow \
-fno-delete-null-pointer-checks \
-fwrapv
AM_CPPFLAGS = \
-D_FORTIFY_SOURCE=2 \
@@ -26,8 +27,8 @@ AM_CPPFLAGS = \
-DABSTOPSRCDIR=\"$(abs_top_srcdir)\" \
-DDATADIR=\"$(datadir)\" \
-DLOCALSTATEDIR=\"$(localstatedir)\" \
-DKERNELOOPSDIR=\"$(localstatedir)/cache/telemetry/oops\" \
-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
+27 -4
View File
@@ -2,13 +2,13 @@
# Process this file with autoconf to produce a configure script.
AC_PREREQ([2.69])
AC_INIT([telemetrics-client], [1.10.2], [https://clearlinux.org/])
AC_INIT([telemetrics-client], [2.4.2], [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])
LT_INIT([disable-static])
AC_CONFIG_MACRO_DIRS([build-aux/m4])
AC_CONFIG_SRCDIR([src/main.c])
AC_CONFIG_SRCDIR([src/probe.c])
AC_CONFIG_HEADERS([config.h])
# Checks for programs.
@@ -20,10 +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.
PKG_CHECK_MODULES([CHECK], [check >= 0.9.12])
# check >= 0.9.12 is required for TAP output
PKG_CHECK_MODULES([CHECK], [check >= 0.12])
PKG_CHECK_MODULES([CURL], [libcurl])
PKG_CHECK_MODULES([GLIB], [glib-2.0])
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"],
@@ -40,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])
@@ -97,6 +108,11 @@ AC_ARG_WITH([systemdsysctldir], AS_HELP_STRING([--with-systemdsysctldir=DIR],
test -z "${sysctlpath}" && sysctlpath=/usr/lib/sysctl.d
AC_SUBST(SYSTEMD_SYSCTLDIR, [${sysctlpath}])
AC_ARG_WITH([systemdsystemctldir], AS_HELP_STRING([--with-systemdsystemctldir=DIR],
[path to systemd systemctl dir @<:@default=/usr/bin@:>@]), [systemctlpath=${withval}])
test -z "${systemctlpath}" && systemctlpath=/usr/bin
AC_SUBST(SYSTEMD_SYSTEMCTLDIR, [${systemctlpath}])
# systemd.pc gives the /etc conf dir, so we can't use PKG_CONFIG for this one
AC_ARG_WITH([systemdsystemconfdir], AS_HELP_STRING([--with-systemdsystemconfdir=DIR],
[path to systemd system conf dir @<:@default=/usr/lib/systemd/system.conf.d@:>@]), [confpath=${withval}],
@@ -113,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
@@ -160,4 +181,6 @@ systemconfdir: $confpath
socketdir: $socketpath
loglevel: $loglevel
logtype: $logtype
valgrind: $enable_valgrind
])
+3 -3
View File
@@ -1,9 +1,9 @@
## Notes on manual pages
The manual pages are generated using `ronn(1)`. To recreate them,
run `make manpages` in the toplevel folder. If you want to edit
the documentation for upstream changes, make sure to edit the `*.md`
The manual pages are generated using `rst2man.py`. To recreate them,
run `make manpages` in the toplevel folder. If you want to edit
the documentation for upstream changes, make sure to edit the `*.rst`
files and not the shipped nroff output files.
+4 -3
View File
@@ -1,6 +1,7 @@
MANPAGES = \
docs/man/telemctl.1 \
docs/man/telemd.1 \
docs/man/telemprobd.1 \
docs/man/telempostd.1 \
docs/man/telem-record-gen.1 \
docs/man/telemetry.3 \
docs/man/telemetrics.conf.5
@@ -13,8 +14,8 @@ MANLINKS = \
manpages:
for MANPAGE in $(MANPAGES); do \
ronn --roff < $${MANPAGE}.md > $${MANPAGE}; \
ronn --html < $${MANPAGE}.md > $${MANPAGE}.html; \
rst2man.py $${MANPAGE}.rst > $${MANPAGE}; \
rst2html.py $${MANPAGE}.rst > $${MANPAGE}.html; \
done
dist_man_MANS = \
+97 -77
View File
@@ -1,81 +1,101 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.\" Man page generated from reStructuredText.
.
.TH "TELEM\-RECORD\-GEN" "1" "December 2016" "" ""
.TH TELEM-RECORD-GEN 1 "" "" ""
.SH NAME
telem-record-gen \- Create custom telemetry record
.
.SH "NAME"
\fBtelem\-record\-gen\fR \- Create custom telemetry record
.nr rst2man-indent-level 0
.
.SH "SYNOPSIS"
\fBtelem\-record\-gen\fR \fIoptions\fR
.de1 rstReportMargin
\\$1 \\n[an-margin]
level \\n[rst2man-indent-level]
level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
-
\\n[rst2man-indent0]
\\n[rst2man-indent1]
\\n[rst2man-indent2]
..
.de1 INDENT
.\" .rstReportMargin pre:
. RS \\$1
. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
. nr rst2man-indent-level +1
.\" .rstReportMargin post:
..
.de UNINDENT
. RE
.\" indent \\n[an-margin]
.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
.nr rst2man-indent-level -1
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.SH SYNOPSIS
.sp
\fBtelem\-record\-gen\fP <options>
.SH DESCRIPTION
.sp
Generate and attempt to send a telemetry record to \fBtelemprobd\fP(1). This
program can be used to create a telemetry record from a shell script
or from another program using e.g. \fBsystem()\fP\&. The program connects
to a local \fBtelemprobd\fP(1) daemon to spool the record for delivery to
the actual collection service.
.sp
The payload can be provided as standard input, as a file with the
\fB\-\-payload\-file\fP option, or as a string argument with the \fB\-\-payload\fP
option. If neither the \fB\-\-payload\fP or \fB\-\-payload\-file\fP is provided,
the payload is assumed to be on <stdin>.
.sp
Each record is expected to have a class identifier, a record version
number, and a severity level. A class identifier is required to be
provided.
.SH OPTIONS
.INDENT 0.0
.INDENT 3.5
.INDENT 0.0
.IP \(bu 2
\fB\-h\fP, \fB\-\-help\fP:
Show help options.
.IP \(bu 2
\fB\-V\fP, \fB\-\-version\fP:
Print the program version.
.IP \(bu 2
\fB\-f\fP, \fB\-\-config\-file\fP <file>:
Path to configuration file (not implemented yet).
.IP \(bu 2
\fB\-s\fP, \fB\-\-severity\fP:
Severity level (1\-4) \- (default 1).
.IP \(bu 2
\fB\-c\fP, \fB\-\-class\fP:
Classification \fBlevel_1\fP/\fBlevel_2\fP/\fBlevel_3\fP\&.
.IP \(bu 2
\fB\-p\fP, \fB\-\-payload\fP <payload>:
Record body (max size = 8k).
.IP \(bu 2
\fB\-P\fP, \fB\-\-payload\-file\fP <file>:
File to read payload from.
.IP \(bu 2
\fB\-R\fP, \fB\-\-record\-version\fP <version>:
Version number for format of payload (default 1).
.IP \(bu 2
\fB\-e\fP, \fB\-\-event\-id\fP <version>:
Event id to use in the record. If not provided a randomly generated id will be assigned to record.
.UNINDENT
.UNINDENT
.UNINDENT
.SH RETURN VALUES
.sp
0 on success. A non\-zero exit code indicates a failure occurred.
.SH SEE ALSO
.INDENT 0.0
.IP \(bu 2
\fBtelemprobd\fP(1)
.IP \(bu 2
\fI\%https://github.com/clearlinux/telemetrics\-client\fP
.IP \(bu 2
\fI\%https://clearlinux.org/documentation/\fP
.UNINDENT
.SH COPYRIGHT
(C) 2017 Intel Corporation, CC-BY-SA-3.0
.\" Generated by docutils manpage writer.
.
.SH "DESCRIPTION"
Generate and attempt to send a telemetry record to \fBtelemd(1)\fR\. This program can be used to create a telemetry record from a shell script or from another program using e\.g\. \fBsystem()\fR\. The program connects to a local \fBtelemd(1)\fR daemon to spool the record for delivery to the actual collection service\.
.
.P
The payload can be provided as standard input, as a file with the \fB\-\-payload\-file\fR option, or as a string argument with the \fB\-\-payload\fR option\. If neither the \fB\-\-payload\fR or \fB\-\-payload\-file\fR is provided, the payload is assumed to be on \fIstdin\fR\.
.
.P
Each record is expected to have a class identifier, a record version number, and a severity level\. A class identifier is required to be provided\.
.
.SH "OPTIONS"
.
.TP
\fB\-h\fR, \fB\-\-help\fR
Show help options\.
.
.TP
\fB\-V\fR, \fB\-\-version\fR
Print the program version\.
.
.TP
\fB\-f\fR, \fB\-\-config\-file\fR \fIfile\fR
Path to configuration file (not implemented yet)\.
.
.TP
\fB\-s\fR, \fB\-\-severity\fR
Severity level (1\-4) \- (default 1)\.
.
.TP
\fB\-c\fR, \fB\-\-class\fR
Classification \fBlevel_1\fR/\fBlevel_2\fR/\fBlevel_3\fR\.
.
.TP
\fB\-p\fR, \fB\-\-payload\fR \fIpayload\fR
Record body (max size = 8k)\.
.
.TP
\fB\-P\fR, \fB\-\-payload\-file\fR \fIfile\fR
File to read payload from\.
.
.TP
\fB\-R\fR, \fB\-\-record\-version\fR \fIversion\fR
Version number for format of payload (default 1)\.
.
.SH "RETURN VALUES"
0 on success\. A non\-zero exit code indicates a failure occurred\.
.
.SH "COPYRIGHT"
.
.IP "\(bu" 4
Copyright (C) 2016 Intel Corporation, License: CC\-BY\-SA\-3\.0
.
.IP "" 0
.
.SH "SEE ALSO"
\fBtelemd(1)\fR
.
.P
https://github\.com/clearlinux/telemetrics\-client
.
.P
https://clearlinux\.org/documentation/
.
.SH "NOTES"
Creative Commons Attribution\-ShareAlike 3\.0 Unported
.
.IP "\(bu" 4
http://creativecommons\.org/licenses/by\-sa/3\.0/
.
.IP "" 0
-70
View File
@@ -1,70 +0,0 @@
telem-record-gen(1) -- Create custom telemetry record
=====================================================
## SYNOPSIS
`telem-record-gen` <options>
## DESCRIPTION
Generate and attempt to send a telemetry record to `telemd(1)`. This
program can be used to create a telemetry record from a shell script
or from another program using e.g. `system()`. The program connects
to a local `telemd(1)` daemon to spool the record for delivery to
the actual collection service.
The payload can be provided as standard input, as a file with the
`--payload-file` option, or as a string argument with the `--payload`
option. If neither the `--payload` or `--payload-file` is provided,
the payload is assumed to be on <stdin>.
Each record is expected to have a class identifier, a record version
number, and a severity level. A class identifier is required to be
provided.
## OPTIONS
* `-h`, `--help`:
Show help options.
* `-V`, `--version`:
Print the program version.
* `-f`, `--config-file` <file>:
Path to configuration file (not implemented yet).
* `-s`, `--severity`:
Severity level (1-4) - (default 1).
* `-c`, `--class`:
Classification `level_1`/`level_2`/`level_3`.
* `-p`, `--payload` <payload>:
Record body (max size = 8k).
* `-P`, `--payload-file` <file>:
File to read payload from.
* `-R`, `--record-version` <version>:
Version number for format of payload (default 1).
## RETURN VALUES
0 on success. A non-zero exit code indicates a failure occurred.
## COPYRIGHT
* Copyright (C) 2016 Intel Corporation, License: CC-BY-SA-3.0
## SEE ALSO
`telemd(1)`
https://github.com/clearlinux/telemetrics-client
https://clearlinux.org/documentation/
## NOTES
Creative Commons Attribution-ShareAlike 3.0 Unported
* http://creativecommons.org/licenses/by-sa/3.0/
+81
View File
@@ -0,0 +1,81 @@
================
telem-record-gen
================
------------------------------
Create custom telemetry record
------------------------------
:Copyright: \(C) 2017 Intel Corporation, CC-BY-SA-3.0
:Manual section: 1
SYNOPSIS
========
``telem-record-gen`` <options>
DESCRIPTION
===========
Generate and attempt to send a telemetry record to ``telemprobd``\(1). This
program can be used to create a telemetry record from a shell script
or from another program using e.g. ``system()``. The program connects
to a local ``telemprobd``\(1) daemon to spool the record for delivery to
the actual collection service.
The payload can be provided as standard input, as a file with the
``--payload-file`` option, or as a string argument with the ``--payload``
option. If neither the ``--payload`` or ``--payload-file`` is provided,
the payload is assumed to be on <stdin>.
Each record is expected to have a class identifier, a record version
number, and a severity level. A class identifier is required to be
provided.
OPTIONS
=======
* ``-h``, ``--help``:
Show help options.
* ``-V``, ``--version``:
Print the program version.
* ``-f``, ``--config-file`` <file>:
Path to configuration file (not implemented yet).
* ``-s``, ``--severity``:
Severity level (1-4) - (default 1).
* ``-c``, ``--class``:
Classification ``level_1``/``level_2``/``level_3``.
* ``-p``, ``--payload`` <payload>:
Record body (max size = 8k).
* ``-P``, ``--payload-file`` <file>:
File to read payload from.
* ``-R``, ``--record-version`` <version>:
Version number for format of payload (default 1).
* ``-e``, ``--event-id`` <version>:
Event id to use in the record. If not provided a randomly generated id will be assigned to record.
RETURN VALUES
=============
0 on success. A non-zero exit code indicates a failure occurred.
SEE ALSO
========
* ``telemprobd``\(1)
* https://github.com/clearlinux/telemetrics-client
* https://clearlinux.org/documentation/
+75 -54
View File
@@ -1,58 +1,79 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.\" Man page generated from reStructuredText.
.
.TH "TELEMCTL" "1" "December 2016" "" ""
.TH TELEMCTL 1 "" "" ""
.SH NAME
telemctl \- Telemetry service administration tool
.
.SH "NAME"
\fBtelemctl\fR \- Telemetry service administration tool
.nr rst2man-indent-level 0
.
.SH "SYNOPSIS"
\fBtelemctl\fR
.de1 rstReportMargin
\\$1 \\n[an-margin]
level \\n[rst2man-indent-level]
level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
-
\\n[rst2man-indent0]
\\n[rst2man-indent1]
\\n[rst2man-indent2]
..
.de1 INDENT
.\" .rstReportMargin pre:
. RS \\$1
. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
. nr rst2man-indent-level +1
.\" .rstReportMargin post:
..
.de UNINDENT
. RE
.\" indent \\n[an-margin]
.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
.nr rst2man-indent-level -1
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.SH SYNOPSIS
.sp
\fBtelemctl\fP
.sp
\fB/etc/telemetrics/opt\-in\fP
.SH DESCRIPTION
.sp
Control actions for telemetry services. The command can be used to start,
restart, or stop \fBtelemprobd\fP(1) and \fBtelempostd\fP(1), or to opt\-in or opt\-out of telemetry delivery of records to a central telemetry service.
.SH OPTIONS
.INDENT 0.0
.INDENT 3.5
.INDENT 0.0
.IP \(bu 2
\fBstart\(ga\(ga|\(ga\(gastop\(ga\(ga|\(ga\(garestart\fP:
Starts, stops or restarts all running telemetry services.
.IP \(bu 2
\fBopt\-in\fP:
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\-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).
.UNINDENT
.UNINDENT
.UNINDENT
.SH RETURN VALUES
.sp
0 on success. A non\-zero exit code indicates a failure occurred.
.SH SEE ALSO
.INDENT 0.0
.IP \(bu 2
\fBtelemprobd\fP(1)
.IP \(bu 2
\fI\%https://github.com/clearlinux/telemetrics\-client\fP
.IP \(bu 2
\fI\%https://clearlinux.org/documentation/\fP
.UNINDENT
.SH COPYRIGHT
(C) 2017 Intel Corporation, CC-BY-SA-3.0
.\" Generated by docutils manpage writer.
.
.P
\fB/etc/telemetrics/opt\-out\fR
.
.SH "DESCRIPTION"
Control actions for telemetry services\. The command can be used to start, restart, or stop \fBtelemd(1)\fR, or to opt\-in or opt\-out of telemetry delivery of records to a central telemetry service\.
.
.SH "OPTIONS"
.
.TP
\fBstart\fR|\fBstop\fR|\fBrestart\fR
Starts, stops or restarts all running telemetry services\.
.
.TP
\fBopt\-in\fR
Opts in to telemetry, and starts telemetry services\. The opt\-out file \fB/etc/telemetrics/opt\-out\fR is removed\.
.
.TP
\fBopt\-out\fR
Opts out of telemetry, and stops telemetry services\. The opt\-out file \fB/etc/telemetrics/opt\-out\fR is created\.
.
.SH "RETURN VALUES"
0 on success\. A non\-zero exit code indicates a failure occurred\.
.
.SH "COPYRIGHT"
.
.IP "\(bu" 4
Copyright (C) 2016 Intel Corporation, License: CC\-BY\-SA\-3\.0
.
.IP "" 0
.
.SH "SEE ALSO"
\fBtelemd(1)\fR
.
.P
https://github\.com/clearlinux/telemetrics\-client
.
.P
https://clearlinux\.org/documentation/
.
.SH "NOTES"
Creative Commons Attribution\-ShareAlike 3\.0 Unported
.
.IP "\(bu" 4
http://creativecommons\.org/licenses/by\-sa/3\.0/
.
.IP "" 0
-49
View File
@@ -1,49 +0,0 @@
telemctl(1) -- Telemetry service administration tool
====================================================
## SYNOPSIS
`telemctl`
`/etc/telemetrics/opt-out`
## DESCRIPTION
Control actions for telemetry services. The command can be used to start,
restart, or stop `telemd(1)`, or to opt-in or opt-out of telemetry delivery
of records to a central telemetry service.
## OPTIONS
* `start`|`stop`|`restart`:
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.
* `opt-out`:
Opts out of telemetry, and stops telemetry services. The opt-out file
`/etc/telemetrics/opt-out` is created.
## RETURN VALUES
0 on success. A non-zero exit code indicates a failure occurred.
## COPYRIGHT
* Copyright (C) 2016 Intel Corporation, License: CC-BY-SA-3.0
## SEE ALSO
`telemd(1)`
https://github.com/clearlinux/telemetrics-client
https://clearlinux.org/documentation/
## NOTES
Creative Commons Attribution-ShareAlike 3.0 Unported
* http://creativecommons.org/licenses/by-sa/3.0/
+58
View File
@@ -0,0 +1,58 @@
========
telemctl
========
-------------------------------------
Telemetry service administration tool
-------------------------------------
:Copyright: \(C) 2017 Intel Corporation, CC-BY-SA-3.0
:Manual section: 1
SYNOPSIS
========
``telemctl``
``/etc/telemetrics/opt-in``
DESCRIPTION
===========
Control actions for telemetry services. The command can be used to start,
restart, or stop ``telemprobd``\(1) and ``telempostd``\(1), or to opt-in or opt-out of telemetry delivery of records to a central telemetry service.
OPTIONS
=======
* ``start``|``stop``|``restart``:
Starts, stops or restarts all running telemetry services.
* ``opt-in``:
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-in file
``/etc/telemetrics/opt-in`` is deleted.
* ``is-active``:
Checks if telemetry client daemons are active (telemprobd and telempostd).
RETURN VALUES
=============
0 on success. A non-zero exit code indicates a failure occurred.
SEE ALSO
========
* ``telemprobd``\(1)
* https://github.com/clearlinux/telemetrics-client
* https://clearlinux.org/documentation/
-55
View File
@@ -1,55 +0,0 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
.TH "TELEMD" "1" "December 2016" "" ""
.
.SH "NAME"
\fBtelemd\fR \- Telemetry client service
.
.SH "SYNOPSIS"
\fBtelemd\fR \fIflags\fR \fB/etc/telemetrics/telemetrics\.conf\fR
.
.SH "DESCRIPTION"
The \fBtelemd\fR program delivers locally generated telemetry records to a remote telemetry service\. Telemetry data can be in any format, and is relayed as\-is\.
.
.SH "OPTIONS"
.
.TP
\fB\-f\fR, \fB\-\-config_file\fR [\fIfile\fR]
Configuration file\. This overides the other parameters\.
.
.TP
\fB\-h\fR, \fB\-\-help\fR
Display this help message\.
.
.TP
\fB\-V\fR, \fB\-\-version\fR
Print the program version\.
.
.SH "EXIT STATUS"
0 when no errors occurred\. A non\-zero exit status indicates a failure occurred\.
.
.SH "COPYRIGHT"
.
.IP "\(bu" 4
Copyright (C) 2016 Intel Corporation, License: CC\-BY\-SA\-3\.0
.
.IP "" 0
.
.SH "SEE ALSO"
\fBtelemetry(3)\fR \fBtelemetrics\-client\.conf(5)\fR
.
.P
https://github\.com/clearlinux/telemetrics\-client
.
.P
https://clearlinux\.org/documentation/
.
.SH "NOTES"
Creative Commons Attribution\-ShareAlike 3\.0 Unported
.
.IP "\(bu" 4
http://creativecommons\.org/licenses/by\-sa/3\.0/
.
.IP "" 0
-47
View File
@@ -1,47 +0,0 @@
telemd(1) -- Telemetry client service
=====================================
## SYNOPSIS
`telemd` <flags>
`/etc/telemetrics/telemetrics.conf`
## DESCRIPTION
The `telemd` program delivers locally generated telemetry records to a remote
telemetry service. Telemetry data can be in any format, and is relayed as-is.
## OPTIONS
* `-f`, `--config_file` [<file>]:
Configuration file. This overides the other parameters.
* `-h`, `--help`:
Display this help message.
* `-V`, `--version`:
Print the program version.
## EXIT STATUS
0 when no errors occurred. A non-zero exit status indicates a failure occurred.
## COPYRIGHT
* Copyright (C) 2016 Intel Corporation, License: CC-BY-SA-3.0
## SEE ALSO
`telemetry(3)`
`telemetrics-client.conf(5)`
https://github.com/clearlinux/telemetrics-client
https://clearlinux.org/documentation/
## NOTES
Creative Commons Attribution-ShareAlike 3.0 Unported
* http://creativecommons.org/licenses/by-sa/3.0/
+123 -101
View File
@@ -1,105 +1,127 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.\" Man page generated from reStructuredText.
.
.TH "TELEMETRICS\.CONF" "5" "December 2016" "" ""
.TH TELEMETRICS.CONF 5 "" "" ""
.SH NAME
telemetrics.conf \- Telemetry service configuration file
.
.SH "NAME"
\fBtelemetrics\.conf\fR \- Telemetry service configuration file
.nr rst2man-indent-level 0
.
.SH "SYNOPSIS"
.de1 rstReportMargin
\\$1 \\n[an-margin]
level \\n[rst2man-indent-level]
level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
-
\\n[rst2man-indent0]
\\n[rst2man-indent1]
\\n[rst2man-indent2]
..
.de1 INDENT
.\" .rstReportMargin pre:
. RS \\$1
. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
. nr rst2man-indent-level +1
.\" .rstReportMargin post:
..
.de UNINDENT
. RE
.\" indent \\n[an-margin]
.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
.nr rst2man-indent-level -1
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.SH SYNOPSIS
.sp
\fB/etc/telemetrics/telemetrics.conf\fP
.sp
\fB/usr/share/defaults/telemetrics/telemetrics.conf\fP
.SH DESCRIPTION
.sp
This file contains configuration parameters for the \fBtelemprobd\fP(1) and \fBtelempostd\fP(1) telemetry service daemons. The daemon reads this file at startup if it exists.
.SH SYNTAX
.sp
The configuration file contains \fBkey=value\fP pairs, formatted as plain
text, one option per line. Comments can be added by preceding them with the
\fB#\fP character. All configuration options should be in a section marked
with \fB[settings]\fP\&.
.SH OPTIONS
.INDENT 0.0
.IP \(bu 2
\fBserver=<url>\fP
.sp
Server URL including protocol designator.
.IP \(bu 2
\fBsocket_path=<path>\fP
.sp
Path to the socket that \fItelemprobd\fP will listen on.
.IP \(bu 2
\fBcainfo=<path>\fP
.sp
Certificate file to use for validation of SSL endpoint.
.IP \(bu 2
\fBtidheader=<header>\fP
.sp
Telemetry ID in header format, usually \fBtidheader=X\-Telemetry\-TID:<uuid>\fP
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.
.IP \(bu 2
\fBrecord_expiry=<minutes>\fP
.sp
Record expiry time in minutes.
.IP \(bu 2
\fBspool_dir=<dir>\fP
.sp
Local spool directory used to store records being processed.
.IP \(bu 2
\fBspool_max_size=<kB>\fP
.sp
maximum size of the spool directory in kB. A value of \fB\-1\fP indicates
no limit. The block size of the files in this directory is considered,
and not the actual size of the record itself.
.IP \(bu 2
\fBspool_process_time=<seconds>\fP
.sp
Time in seconds for processing spool. Valid range: 120..300. Values
outside this range are clamped.
.IP \(bu 2
\fBrate_limit_enabled=<true|false>\fP
.sp
Enable rate limiting. If this is set to false then all rate\-limiting
disabled. It is possible to disable each rate\-limit individually below.
.IP \(bu 2
\fBrecord_burst_limit=<limit>\fP
.sp
Rate limiting record burst limit. Valid Range: 0..\(ga\(gaINT_MAX\(ga\(ga, \-1 = disabled.
.IP \(bu 2
\fBrecord_window_length=<minutes>\fP
.sp
Rate limiting record window length in minutes. Valid Range: 0..59.
.IP \(bu 2
\fBbyte_burst_limit=<limit>\fP
.sp
Rate limiting byte burst limit. Valid Range: 0..\(gaINT_MAX\(ga, \-1 = disabled.
.IP \(bu 2
\fBbyte_window_length=<minutes>\fP
.sp
Rate limiting byte window length in minutes. Valid Range: 0..59.
.IP \(bu 2
\fBrate_limit_strategy=<strategy>\fP
.sp
Rate limit strategy \- what to do with record if rate\-limiting prevents
delivery over network. Valid stategies: \fBspool\fP, \fBdrop\fP\&.
.UNINDENT
.SH SEE ALSO
.INDENT 0.0
.IP \(bu 2
\fBtelemprobd\fP(1)
.IP \(bu 2
\fBtelempostd\fP(1)
.IP \(bu 2
\fI\%https://github.com/clearlinux/telemetrics\-client\fP
.IP \(bu 2
\fI\%https://clearlinux.org/documentation/\fP
.UNINDENT
.SH COPYRIGHT
(C) 2017 Intel Corporation, CC-BY-SA-3.0
.\" Generated by docutils manpage writer.
.
.nf
/etc/telemetrics/telemetrics\.conf
/usr/share/defaults/telemetrics/telemetrics\.conf
.
.fi
.
.SH "DESCRIPTION"
This file contains configuration parameters for the \fBtelemd(1)\fR telemetry service daemon\. The daemon reads this file at startup if it exists\.
.
.SH "SYNTAX"
The configuration file contains \fBkey=value\fR pairs, formatted as plain text, one option per line\. Comments can be added by preceding them with the \fB#\fR character\. All configuration options should be in a section marked with \fB[settings]\fR\.
.
.SH "OPTIONS"
.
.TP
\fBserver\fR=\fIurl\fR
Server URL including protocol designator\.
.
.TP
\fBsocket_path\fR=\fIpath\fR
Path to the socket that \fBtelemd\fR will listen on\.
.
.TP
\fBcainfo\fR=\fIpath\fR
Certificate file to use for validation of SSL endpoint\.
.
.TP
\fBtidheader\fR=\fIheader\fR
Telemetry ID in header format, usually \fBtidheader=X\-Telemetry\-TID:<uuid>\fR 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\.
.
.TP
\fBrecord_expiry\fR=\fIminutes\fR
Record expiry time in minutes\.
.
.TP
\fBspool_dir\fR=\fI\fBdir\fR\fR
Local spool directory used to store records being processed\.
.
.TP
\fBspool_max_size\fR=\fIkB\fR
maximum size of the spool directory in kB\. A value of \fB\-1\fR indicates no limit\. The block size of the files in this directory is considered, and not the actual size of the record itself\.
.
.TP
\fBspool_process_time\fR=\fIseconds\fR
Time in seconds for processing spool\. Valid range: 120\.\.3600\. Values outside this range are clamped\.
.
.TP
\fBrate_limit_enabled\fR=\fI\fBtrue\fR|\fBfalse\fR\fR
Enable rate limiting\. If this is set to false then all rate\-limiting disabled\. It is possible to disable each rate\-limit individually below\.
.
.TP
\fBrecord_burst_limit\fR=\fIlimit\fR
Rate limiting record burst limit\. Valid Range: 0\.\.\fBINT_MAX\fR, \-1 = disabled\.
.
.TP
\fBrecord_window_length\fR=\fIminutes\fR
Rate limiting record window length in minutes\. Valid Range: 0\.\.59\.
.
.TP
\fBbyte_burst_limit\fR=\fIlimit\fR
Rate limiting byte burst limit\. Valid Range: 0\.\.\fBINT_MAX\fR, \-1 = disabled\.
.
.TP
\fBbyte_window_length\fR=\fIminutes\fR
Rate limiting byte window length in minutes\. Valid Range: 0\.\.59\.
.
.TP
\fBrate_limit_strategy\fR=\fIstrategy\fR
Rate limit strategy \- what to do with record if rate\-limiting prevents delivery over network\. Valid stategies: \fBspool\fR, \fBdrop\fR\.
.
.SH "COPYRIGHT"
.
.IP "\(bu" 4
Copyright (C) 2016 Intel Corporation, License: CC\-BY\-SA\-3\.0
.
.IP "" 0
.
.SH "SEE ALSO"
\fBtelemd(1)\fR
.
.P
https://github\.com/clearlinux/telemetrics\-client
.
.P
https://clearlinux\.org/documentation/
.
.SH "NOTES"
Creative Commons Attribution\-ShareAlike 3\.0 Unported
.
.IP "\(bu" 4
http://creativecommons\.org/licenses/by\-sa/3\.0/
.
.IP "" 0
-92
View File
@@ -1,92 +0,0 @@
telemetrics.conf(5) -- Telemetry service configuration file
===========================================================
## SYNOPSIS
```
/etc/telemetrics/telemetrics.conf
/usr/share/defaults/telemetrics/telemetrics.conf
```
## DESCRIPTION
This file contains configuration parameters for the `telemd(1)` telemetry
service daemon. The daemon reads this file at startup if it exists.
## SYNTAX
The configuration file contains `key=value` pairs, formatted as plain
text, one option per line. Comments can be added by preceding them with the
`#` character. All configuration options should be in a section marked
with `[settings]`.
## OPTIONS
* `server`=<url>:
Server URL including protocol designator.
* `socket_path`=<path>:
Path to the socket that `telemd` will listen on.
* `cainfo`=<path>:
Certificate file to use for validation of SSL endpoint.
* `tidheader`=<header>:
Telemetry ID in header format, usually
`tidheader=X-Telemetry-TID:<uuid>`
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.
* `record_expiry`=<minutes>:
Record expiry time in minutes.
* `spool_dir`=<`dir`>:
Local spool directory used to store records being processed.
* `spool_max_size`=<kB>:
maximum size of the spool directory in kB. A value of `-1` indicates
no limit. The block size of the files in this directory is considered,
and not the actual size of the record itself.
* `spool_process_time`=<seconds>:
Time in seconds for processing spool. Valid range: 120..3600. Values
outside this range are clamped.
* `rate_limit_enabled`=<`true`|`false`>:
Enable rate limiting. If this is set to false then all rate-limiting
disabled. It is possible to disable each rate-limit individually below.
* `record_burst_limit`=<limit>:
Rate limiting record burst limit. Valid Range: 0..`INT_MAX`, -1 = disabled.
* `record_window_length`=<minutes>:
Rate limiting record window length in minutes. Valid Range: 0..59.
* `byte_burst_limit`=<limit>:
Rate limiting byte burst limit. Valid Range: 0..`INT_MAX`, -1 = disabled.
* `byte_window_length`=<minutes>:
Rate limiting byte window length in minutes. Valid Range: 0..59.
* `rate_limit_strategy`=<strategy>:
Rate limit strategy - what to do with record if rate-limiting prevents
delivery over network. Valid stategies: `spool`, `drop`.
## COPYRIGHT
* Copyright (C) 2016 Intel Corporation, License: CC-BY-SA-3.0
## SEE ALSO
`telemd(1)`
https://github.com/clearlinux/telemetrics-client
https://clearlinux.org/documentation/
## NOTES
Creative Commons Attribution-ShareAlike 3.0 Unported
* http://creativecommons.org/licenses/by-sa/3.0/
+109
View File
@@ -0,0 +1,109 @@
================
telemetrics.conf
================
------------------------------------
Telemetry service configuration file
------------------------------------
:Copyright: \(C) 2017 Intel Corporation, CC-BY-SA-3.0
:Manual section: 5
SYNOPSIS
========
``/etc/telemetrics/telemetrics.conf``
``/usr/share/defaults/telemetrics/telemetrics.conf``
DESCRIPTION
===========
This file contains configuration parameters for the ``telemprobd``\(1) and ``telempostd``\(1) telemetry service daemons. The daemon reads this file at startup if it exists.
SYNTAX
======
The configuration file contains ``key=value`` pairs, formatted as plain
text, one option per line. Comments can be added by preceding them with the
``#`` character. All configuration options should be in a section marked
with ``[settings]``.
OPTIONS
=======
- ``server=<url>``
Server URL including protocol designator.
- ``socket_path=<path>``
Path to the socket that `telemprobd` will listen on.
- ``cainfo=<path>``
Certificate file to use for validation of SSL endpoint.
- ``tidheader=<header>``
Telemetry ID in header format, usually ``tidheader=X-Telemetry-TID:<uuid>``
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.
- ``record_expiry=<minutes>``
Record expiry time in minutes.
- ``spool_dir=<dir>``
Local spool directory used to store records being processed.
- ``spool_max_size=<kB>``
maximum size of the spool directory in kB. A value of ``-1`` indicates
no limit. The block size of the files in this directory is considered,
and not the actual size of the record itself.
- ``spool_process_time=<seconds>``
Time in seconds for processing spool. Valid range: 120..300. Values
outside this range are clamped.
- ``rate_limit_enabled=<true|false>``
Enable rate limiting. If this is set to false then all rate-limiting
disabled. It is possible to disable each rate-limit individually below.
- ``record_burst_limit=<limit>``
Rate limiting record burst limit. Valid Range: 0..``INT_MAX``, -1 = disabled.
- ``record_window_length=<minutes>``
Rate limiting record window length in minutes. Valid Range: 0..59.
- ``byte_burst_limit=<limit>``
Rate limiting byte burst limit. Valid Range: 0..`INT_MAX`, -1 = disabled.
- ``byte_window_length=<minutes>``
Rate limiting byte window length in minutes. Valid Range: 0..59.
- ``rate_limit_strategy=<strategy>``
Rate limit strategy - what to do with record if rate-limiting prevents
delivery over network. Valid stategies: ``spool``, ``drop``.
SEE ALSO
========
* ``telemprobd``\(1)
* ``telempostd``\(1)
* https://github.com/clearlinux/telemetrics-client
* https://clearlinux.org/documentation/
+86 -65
View File
@@ -1,69 +1,90 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.\" Man page generated from reStructuredText.
.
.TH "TELEMETRY" "3" "December 2016" "" ""
.TH TELEMETRY 3 "" "" ""
.SH NAME
telemetry \- C programming interface for telemetrics-client
.
.SH "NAME"
\fBtelemetry\fR \- C programming interface for telemetrics\-client
.nr rst2man-indent-level 0
.
.SH "SYNOPSIS"
.de1 rstReportMargin
\\$1 \\n[an-margin]
level \\n[rst2man-indent-level]
level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
-
\\n[rst2man-indent0]
\\n[rst2man-indent1]
\\n[rst2man-indent2]
..
.de1 INDENT
.\" .rstReportMargin pre:
. RS \\$1
. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
. nr rst2man-indent-level +1
.\" .rstReportMargin post:
..
.de UNINDENT
. RE
.\" indent \\n[an-margin]
.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
.nr rst2man-indent-level -1
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.SH SYNOPSIS
.sp
\fB#include "telemetry.h"\fP
.sp
\fBstruct telem_ref { struct telem_record *record; };\fP
.sp
\fBint tm_create_record(struct telem_ref **t_ref, uint32_t severity, char *classification, uint32_t payload_version)\fP
.sp
\fBint tm_set_payload(struct telem_ref *t_ref, char *payload)\fP
.sp
\fBint tm_send_record(struct telem_ref *t_ref)\fP
.sp
\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
telemetry data to the \fBtelemprobd\fP(1) service.
.sp
The function \fBtm_create_record()\fP initializes a telemetry record and
sets the severity and classification of that record, as well as the
payload version number. The memory needed to store the telemetry record
is allocated and should be freed with \fBtm_free_record()\fP when no longer
needed.
.sp
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
\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. \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
\fBtelemprobd\fP(1)
.IP \(bu 2
\fI\%https://github.com/clearlinux/telemetrics\-client\fP
.IP \(bu 2
\fI\%https://clearlinux.org/documentation/\fP
.UNINDENT
.SH COPYRIGHT
(C) 2017 Intel Corporation, CC-BY-SA-3.0
.\" Generated by docutils manpage writer.
.
.nf
#include "telemetry\.h"
struct telem_ref {
struct telem_record *record;
};
int tm_create_record(struct telem_ref **t_ref, uint32_t severity,
char *classification, uint32_t payload_version)
int tm_set_payload(struct telem_ref *t_ref, char *payload)
int tm_send_record(struct telem_ref *t_ref)
void tm_free_record(struct telem_ref *t_ref)
void tm_set_config_file(char *c_file)
.
.fi
.
.SH "DESCRIPTION"
The functions in the telemetry library facilitate the delivery of telemetry data to the \fBtelemd(1)\fR service\.
.
.P
The function \fBtm_create_record()\fR initializes a telemetry record and sets the severity and classification of that record, as well as the payload version number\. The memory needed to store the telemetry record is allocated and should be freed with \fBtm_free_record()\fR when no longer needed\.
.
.P
The function \fBtm_set_payload()\fR attaches the provided telemetry record data to the telemetry record\. The current maximum payload size is 8192b\.
.
.P
The function \fBtm_send_record()\fR delivers the record to the local \fBtelemd(1)\fR service\.
.
.P
The function \fBtm_set_config_file()\fR can be used to provide an alternate configuration path to the telemetry library\.
.
.SH "RETURN VALUES"
All these functions return \fB0\fR on success, or a non\-zero return value if an error occurred\. The functions \fBtm_free_record()\fR and \fBtm_set_config_file()\fR do not return any values\.
.
.SH "COPYRIGHT"
.
.IP "\(bu" 4
Copyright (C) 2016 Intel Corporation, License: CC\-BY\-SA\-3\.0
.
.IP "" 0
.
.SH "SEE ALSO"
\fBtelemd(1)\fR
.
.P
https://github\.com/clearlinux/telemetrics\-client
.
.P
https://clearlinux\.org/documentation/
.
.SH "NOTES"
Creative Commons Attribution\-ShareAlike 3\.0 Unported
.
.IP "\(bu" 4
http://creativecommons\.org/licenses/by\-sa/3\.0/
.
.IP "" 0
-63
View File
@@ -1,63 +0,0 @@
telemetry(3) -- C programming interface for telemetrics-client
==============================================================
## SYNOPSIS
```
#include "telemetry.h"
struct telem_ref {
struct telem_record *record;
};
int tm_create_record(struct telem_ref **t_ref, uint32_t severity,
char *classification, uint32_t payload_version)
int tm_set_payload(struct telem_ref *t_ref, char *payload)
int tm_send_record(struct telem_ref *t_ref)
void tm_free_record(struct telem_ref *t_ref)
void tm_set_config_file(char *c_file)
```
## DESCRIPTION
The functions in the telemetry library facilitate the delivery of
telemetry data to the `telemd(1)` service.
The function `tm_create_record()` initializes a telemetry record and
sets the severity and classification of that record, as well as the
payload version number. The memory needed to store the telemetry record
is allocated and should be freed with `tm_free_record()` when no longer
needed.
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.
The function `tm_set_config_file()` can be used to provide an alternate
configuration path to the telemetry library.
## RETURN VALUES
All these functions return `0` on success, or a non-zero return value
if an error occurred. The functions `tm_free_record()` and `tm_set_config_file()`
do not return any values.
## COPYRIGHT
* Copyright (C) 2016 Intel Corporation, License: CC-BY-SA-3.0
## SEE ALSO
`telemd(1)`
https://github.com/clearlinux/telemetrics-client
https://clearlinux.org/documentation/
## NOTES
Creative Commons Attribution-ShareAlike 3.0 Unported
* http://creativecommons.org/licenses/by-sa/3.0/
+71
View File
@@ -0,0 +1,71 @@
=========
telemetry
=========
----------------------------------------------
C programming interface for telemetrics-client
----------------------------------------------
:Copyright: \(C) 2017 Intel Corporation, CC-BY-SA-3.0
:Manual section: 3
SYNOPSIS
========
``#include "telemetry.h"``
``struct telem_ref { struct telem_record *record; };``
``int tm_create_record(struct telem_ref **t_ref, uint32_t severity, char *classification, uint32_t payload_version)``
``int tm_set_payload(struct telem_ref *t_ref, char *payload)``
``int tm_send_record(struct telem_ref *t_ref)``
``void tm_free_record(struct telem_ref *t_ref)``
``int tm_set_config_file(const char *c_file)``
``int tm_is_opted_in(void)``
DESCRIPTION
===========
The functions in the telemetry library facilitate the delivery of
telemetry data to the ``telemprobd``\(1) service.
The function ``tm_create_record()`` initializes a telemetry record and
sets the severity and classification of that record, as well as the
payload version number. The memory needed to store the telemetry record
is allocated and should be freed with ``tm_free_record()`` when no longer
needed.
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
``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. ``tm_is_opted_in`` returns ``1`` when telemetry is opted-in
otherwise ``0``.
SEE ALSO
========
* ``telemprobd``\(1)
* https://github.com/clearlinux/telemetrics-client
* https://clearlinux.org/documentation/
+97
View File
@@ -0,0 +1,97 @@
.\" Man page generated from reStructuredText.
.
.TH TELEMPOSTD 1 "" "" ""
.SH NAME
telempostd \- Telemetry client service
.
.nr rst2man-indent-level 0
.
.de1 rstReportMargin
\\$1 \\n[an-margin]
level \\n[rst2man-indent-level]
level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
-
\\n[rst2man-indent0]
\\n[rst2man-indent1]
\\n[rst2man-indent2]
..
.de1 INDENT
.\" .rstReportMargin pre:
. RS \\$1
. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
. nr rst2man-indent-level +1
.\" .rstReportMargin post:
..
.de UNINDENT
. RE
.\" indent \\n[an-margin]
.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
.nr rst2man-indent-level -1
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.SH SYNOPSIS
.sp
\fBtelempostd\fP <flags>
.sp
\fB/etc/telemetrics/telemetrics.conf\fP
.sp
\fB/usr/share/defaults/telemetrics/telemetrics.conf\fP
.sp
\fB/etc/telemetrics/opt\-in\-static\-machine\-id\fP
.SH DESCRIPTION
.sp
The \fBtelempostd\fP program delivers locally generated telemetry records to a remote
telemetry service. Telemetry data can be in any format, and is relayed as\-is.
.SH OPTIONS
.INDENT 0.0
.INDENT 3.5
.INDENT 0.0
.IP \(bu 2
\fB\-f\fP, \fB\-\-config_file\fP [<file>]:
Configuration file. This overrides the other parameters.
.IP \(bu 2
\fB\-h\fP, \fB\-\-help\fP:
Display this help message.
.IP \(bu 2
\fB\-V\fP, \fB\-\-version\fP:
Print the program version.
.UNINDENT
.UNINDENT
.UNINDENT
.SH FILES
.INDENT 0.0
.IP \(bu 2
\fB/usr/share/defaults/telemetrics/telemetrics.conf\fP
.INDENT 2.0
.INDENT 3.5
If no custom configuration file is found, \fBtelempostd\fP uses the
settings in this file.
.UNINDENT
.UNINDENT
.IP \(bu 2
\fB/etc/telemetrics/telemetrics.conf\fP
.INDENT 2.0
.INDENT 3.5
Custom configuration file that \fBtelempostd\fP reads. See \fBtelemetrics.conf\fP(5).
.UNINDENT
.UNINDENT
.UNINDENT
.SH EXIT STATUS
.sp
0 when no errors occurred. A non\-zero exit status indicates a failure occurred.
.SH SEE ALSO
.INDENT 0.0
.IP \(bu 2
\fBtelemetry\fP(3)
.IP \(bu 2
\fBtelemetrics.conf\fP(5)
.IP \(bu 2
\fI\%https://github.com/clearlinux/telemetrics\-client\fP
.IP \(bu 2
\fI\%https://clearlinux.org/documentation/\fP
.UNINDENT
.SH COPYRIGHT
(C) 2018 Intel Corporation, CC-BY-SA-3.0
.\" Generated by docutils manpage writer.
.
+71
View File
@@ -0,0 +1,71 @@
==========
telempostd
==========
------------------------
Telemetry client service
------------------------
:Copyright: \(C) 2018 Intel Corporation, CC-BY-SA-3.0
:Manual section: 1
SYNOPSIS
========
``telempostd`` \<flags\>
``/etc/telemetrics/telemetrics.conf``
``/usr/share/defaults/telemetrics/telemetrics.conf``
``/etc/telemetrics/opt-in-static-machine-id``
DESCRIPTION
===========
The ``telempostd`` program delivers locally generated telemetry records to a remote
telemetry service. Telemetry data can be in any format, and is relayed as-is.
OPTIONS
=======
* ``-f``, ``--config_file`` \[\<file\>\]:
Configuration file. This overrides the other parameters.
* ``-h``, ``--help``:
Display this help message.
* ``-V``, ``--version``:
Print the program version.
FILES
=====
* ``/usr/share/defaults/telemetrics/telemetrics.conf``
If no custom configuration file is found, ``telempostd`` uses the
settings in this file.
* ``/etc/telemetrics/telemetrics.conf``
Custom configuration file that ``telempostd`` reads. See ``telemetrics.conf``\(5).
EXIT STATUS
===========
0 when no errors occurred. A non-zero exit status indicates a failure occurred.
SEE ALSO
========
* ``telemetry``\(3)
* ``telemetrics.conf``\(5)
* https://github.com/clearlinux/telemetrics-client
* https://clearlinux.org/documentation/
+106
View File
@@ -0,0 +1,106 @@
.\" Man page generated from reStructuredText.
.
.TH TELEMPROBD 1 "" "" ""
.SH NAME
telemprobd \- Telemetry probes service
.
.nr rst2man-indent-level 0
.
.de1 rstReportMargin
\\$1 \\n[an-margin]
level \\n[rst2man-indent-level]
level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
-
\\n[rst2man-indent0]
\\n[rst2man-indent1]
\\n[rst2man-indent2]
..
.de1 INDENT
.\" .rstReportMargin pre:
. RS \\$1
. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
. nr rst2man-indent-level +1
.\" .rstReportMargin post:
..
.de UNINDENT
. RE
.\" indent \\n[an-margin]
.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
.nr rst2man-indent-level -1
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.SH SYNOPSIS
.sp
\fBtelemprobd\fP <flags>
.sp
\fB/etc/telemetrics/telemetrics.conf\fP
.sp
\fB/usr/share/defaults/telemetrics/telemetrics.conf\fP
.sp
\fB/etc/telemetrics/opt\-in\-static\-machine\-id\fP
.SH DESCRIPTION
.sp
The \fBtelemprobd\fP program handles communication between telemetry client and telemetry
probes.
.SH OPTIONS
.INDENT 0.0
.INDENT 3.5
.INDENT 0.0
.IP \(bu 2
\fB\-f\fP, \fB\-\-config_file\fP [<file>]:
Configuration file. This overides the other parameters.
.IP \(bu 2
\fB\-h\fP, \fB\-\-help\fP:
Display this help message.
.IP \(bu 2
\fB\-V\fP, \fB\-\-version\fP:
Print the program version.
.UNINDENT
.UNINDENT
.UNINDENT
.SH FILES
.INDENT 0.0
.IP \(bu 2
\fB/usr/share/defaults/telemetrics/telemetrics.conf\fP
.INDENT 2.0
.INDENT 3.5
If no custom configuration file is found, \fBtelemprobd\fP uses the
settings in this file.
.UNINDENT
.UNINDENT
.IP \(bu 2
\fB/etc/telemetrics/telemetrics.conf\fP
.INDENT 2.0
.INDENT 3.5
Custom configuration file that \fBtelemprobd\fP reads. See \fBtelemetrics.conf\fP(5).
.UNINDENT
.UNINDENT
.IP \(bu 2
\fB/etc/telemetrics/opt\-in\-static\-machine\-id\fP
.INDENT 2.0
.INDENT 3.5
If this file exists, the first line of this file will be sent to
the server as the machine ID. Otherwise, the machine ID will be
random and changed regularly.
.UNINDENT
.UNINDENT
.UNINDENT
.SH EXIT STATUS
.sp
0 when no errors occurred. A non\-zero exit status indicates a failure occurred.
.SH SEE ALSO
.INDENT 0.0
.IP \(bu 2
\fBtelemetry\fP(3)
.IP \(bu 2
\fBtelemetrics.conf\fP(5)
.IP \(bu 2
\fI\%https://github.com/clearlinux/telemetrics\-client\fP
.IP \(bu 2
\fI\%https://clearlinux.org/documentation/\fP
.UNINDENT
.SH COPYRIGHT
(C) 2017 Intel Corporation, CC-BY-SA-3.0
.\" Generated by docutils manpage writer.
.
+78
View File
@@ -0,0 +1,78 @@
==========
telemprobd
==========
------------------------
Telemetry probes service
------------------------
:Copyright: \(C) 2017 Intel Corporation, CC-BY-SA-3.0
:Manual section: 1
SYNOPSIS
========
``telemprobd`` \<flags\>
``/etc/telemetrics/telemetrics.conf``
``/usr/share/defaults/telemetrics/telemetrics.conf``
``/etc/telemetrics/opt-in-static-machine-id``
DESCRIPTION
===========
The ``telemprobd`` program handles communication between telemetry client and telemetry
probes.
OPTIONS
=======
* ``-f``, ``--config_file`` \[\<file\>\]:
Configuration file. This overides the other parameters.
* ``-h``, ``--help``:
Display this help message.
* ``-V``, ``--version``:
Print the program version.
FILES
=====
* ``/usr/share/defaults/telemetrics/telemetrics.conf``
If no custom configuration file is found, ``telemprobd`` uses the
settings in this file.
* ``/etc/telemetrics/telemetrics.conf``
Custom configuration file that ``telemprobd`` reads. See ``telemetrics.conf``\(5).
* ``/etc/telemetrics/opt-in-static-machine-id``
If this file exists, the first line of this file will be sent to
the server as the machine ID. Otherwise, the machine ID will be
random and changed regularly.
EXIT STATUS
===========
0 when no errors occurred. A non-zero exit status indicates a failure occurred.
SEE ALSO
========
* ``telemetry``\(3)
* ``telemetrics.conf``\(5)
* https://github.com/clearlinux/telemetrics-client
* https://clearlinux.org/documentation/
-3
View File
@@ -1,3 +0,0 @@
dist_bin_SCRIPTS = %D%/telemctl
# vim: filetype=automake tabstop=8 shiftwidth=8 noexpandtab
+1 -1
View File
@@ -14,6 +14,6 @@ if [ "$?" -ne 0 ]; then
exit 1
fi
find .. -path "../*/*.[ch]" -exec uncrustify {} -c tm.cfg --no-backup \;
find .. -path "../*/*.[ch]" ! -path "../src/nica/*" -exec uncrustify {} -c tm.cfg --no-backup \;
# vi: ts=8 sw=2 sts=2 et tw=80
-115
View File
@@ -1,115 +0,0 @@
#!/bin/bash
declare -a SPECIAL_UNITS=(
hprobe.timer
telemd.socket
telemd.path
)
declare -a SERVICES=(
hprobe.service
journal-probe.service
oops-probe.service
pstore-probe.service
klogscanner.service
telemd.service
)
SCRIPT="$0"
TELEM_DIR=/etc/telemetrics
OPT_OUT_FILE=${TELEM_DIR}/opt-out
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."
# the units in SERVICES are activated as needed, so no need to start them
for_each_service "start" ${SPECIAL_UNITS[@]}
}
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_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
}
usage() {
printf "$SCRIPT - Control actions for telemetry services\n"
printf "\n"
printf "%15s %-65s\n" "stop" "Stops all running telemetry services"
printf "%15s %-65s\n" "start" "Starts all telemetry services"
printf "%15s %-65s\n" "restart" "Restarts all telemetry services"
printf "%15s %-65s\n" "opt-in" "Opts in to telemetry, and starts telemetry services"
printf "%15s %-65s\n" "opt-out" "Opts out of telemetry, and stops telemetry services"
printf "\n"
exit 2
}
if [ $# -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 ;;
*)
notice "Unknown command passed to $SCRIPT"
usage ;;
esac
exit 0
# vi: ts=8 sw=2 sts=2 et tw=80
+5 -1
View File
@@ -33,7 +33,11 @@ static const char *header_names[] = {
TM_SYSTEM_BUILD_STR,
TM_KERNEL_VERSION_STR,
TM_PAYLOAD_VERSION_STR,
TM_SYSTEM_NAME_STR
TM_SYSTEM_NAME_STR,
TM_BOARD_NAME_STR,
TM_CPU_MODEL_STR,
TM_BIOS_VERSION_STR,
TM_EVENT_ID_STR
};
const char *get_header_name(int ind)
+29 -10
View File
@@ -35,6 +35,10 @@
#define TM_KERNEL_VERSION 8
#define TM_PAYLOAD_VERSION 9
#define TM_SYSTEM_NAME 10
#define TM_BOARD_NAME 11
#define TM_CPU_MODEL 12
#define TM_BIOS_VERSION 13
#define TM_EVENT_ID 14
#define TM_RECORD_VERSION_STR "record_format_version"
#define TM_CLASSIFICATION_STR "classification"
@@ -47,22 +51,34 @@
#define TM_KERNEL_VERSION_STR "kernel_version"
#define TM_PAYLOAD_VERSION_STR "payload_format_version"
#define TM_SYSTEM_NAME_STR "system_name"
#define TM_BOARD_NAME_STR "board_name"
#define TM_CPU_MODEL_STR "cpu_model"
#define TM_BIOS_VERSION_STR "bios_version"
#define TM_EVENT_ID_STR "event_id"
#define NUM_HEADERS 11
#define NUM_HEADERS 15
#define EVENT_ID_ALPHAB "0123456789abcdef"
#define EVENT_ID_LEN 32
/* Journal */
#define JOURNAL_PATH "/var/log/telemetry/journal"
#define JOURNAL_TMPDIR "/var/log/telemetry/"
#define RECORD_RETENTION_DIR "/var/log/telemetry/records"
/* For internal library usage. Bump the version whenever we change the record
* structure (e.g. adding or removing a header field). Note that the value
* should be an unsigned int.
*/
static const uint32_t RECORD_FORMAT_VERSION = 2;
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_SIZE 8192
#define MAX_PAYLOAD_LENGTH 8192
/* Maximum size for a category string is 122 bytes */
#define MAX_CLASS_LENGTH 122
@@ -70,12 +86,12 @@ static const uint32_t RECORD_FORMAT_VERSION = 2;
/* Maximum size for a sub category 40 bytes */
#define MAX_SUBCAT_LENGTH 40
/* Max Payload size is 8 kibabytes (8192) */
#define MAX_PAYLOAD_LENGTH 8192
/* Exit daemon after idle for this duration in seconds */
#define TM_DAEMON_EXIT_TIME (5 * 60)
/* SPOOL DEFINES */
/* Spooling should run every TM_SPOOL_RUN_MAX atleast */
#define TM_SPOOL_RUN_MAX (60 /*min*/ * 60 /*sec*/)
/* Spooling should run at least every TM_SPOOL_RUN_MAX */
#define TM_SPOOL_RUN_MAX TM_DAEMON_EXIT_TIME
/* Spooling should not run more often than TM_SPOOL_RUN_MIN */
#define TM_SPOOL_RUN_MIN (2 /*min*/ * 60 /*sec*/)
@@ -86,8 +102,10 @@ static const uint32_t RECORD_FORMAT_VERSION = 2;
/* Maximum records that can be processed in a single spool run loop*/
#define TM_SPOOL_MAX_PROCESS_RECORDS 60
/* Time to check to exit from the daemon */
#define TM_DAEMON_EXIT_TIME (2 /*h*/ * 60 /*m*/ * 60 /*sec*/)
/* 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.
@@ -101,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: */
+189 -68
View File
@@ -16,40 +16,73 @@
#include <stdio.h>
#include <stdlib.h>
#include <glib.h>
#include <errno.h>
#include <limits.h>
#include <dirent.h>
#include <string.h>
#include <ctype.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include "configuration.h"
#include "util.h"
#include "common.h"
#include "log.h"
#include "nica/inifile.h"
static char *config_file = NULL;
static char *default_config_file = DATADIR "/defaults/telemetrics/telemetrics.conf";
static char *etc_config_file = "/etc/telemetrics/telemetrics.conf";
static GKeyFile *keyfile = NULL;
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", NULL };
static struct configuration config = { { 0 }, { 0 }, { 0 }, false, NULL };
int validate_config_file(char *f)
static int validate_config_file(const char *f)
{
struct stat sbuf;
@@ -57,75 +90,146 @@ int validate_config_file(char *f)
return -EINVAL;
}
/* check if the file exists and is a regular file */
if (stat(f, &sbuf) != 0) {
return -errno;
}
if (!S_ISREG(sbuf.st_mode)) {
return -EINVAL;
}
return 0;
}
void set_config_file(char *filename)
const char *get_config_file(void)
{
return config_file;
}
/* TODO: This should return a status, but that will require coordinating
* an API change. For now, config-entries referencing a null config
* file will fail, so the end result still works. */
const char *get_cmd_line_config_file(void)
{
if (cmd_line_cfg == true) {
return config_file;
}
return NULL;
}
if (validate_config_file(filename) == 0) {
config_file = strdup(filename);
cmd_line_cfg = true;
int set_config_file(const char *filename)
{
int ret;
if ((ret = validate_config_file(filename)) == 0) {
char *filename1 = strdup(filename);
if (filename1 == NULL) {
ret = -errno;
} else {
config_file = filename1;
cmd_line_cfg = true;
}
}
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)
{
GError *error = NULL;
GKeyFileFlags flags = G_KEY_FILE_NONE;
if (keyfile != NULL) {
g_key_file_free(keyfile);
nc_hashmap_free(keyfile);
}
keyfile = g_key_file_new();
if (!g_key_file_load_from_file(keyfile, (gchar *)config_file, flags, &error)) {
#ifdef DEBUG
fprintf(stderr, "ERR: Failed to read config file: %s\n",
error->message);
#endif
keyfile = nc_ini_file_parse(config_file);
if (!keyfile) {
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++) {
config->strValues[i] = g_key_file_get_string(keyfile,
"settings", config_key_str[i], &error);
if (error != NULL) {
return false;
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 {
config->strValues[i] = strdup(config_str_default[i]);
}
}
for (int i = CONF_INT_MIN + 1; i < CONF_INT_MAX; i++) {
config->intValues[i] = g_key_file_get_int64(keyfile,
"settings", config_key_int[i], &error);
if (error != NULL) {
return false;
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 {
config->intValues[i] = config_int_default[i];
}
}
for (int i = CONF_BOOL_MIN + 1; i < CONF_BOOL_MAX; i++) {
config->boolValues[i] = g_key_file_get_boolean(keyfile,
"settings", config_key_bool[i], &error);
if (error != NULL) {
return false;
for (int i = 0; i < CONF_BOOL_MAX; i++) {
char *ptr;
ptr = nc_hashmap_get(nc_hashmap_get(keyfile, "settings"), config_key_bool[i]);
if (ptr) {
if ((strcasecmp(ptr, "TRUE") == 0) || (strcmp(ptr, "1") == 0)) {
config->boolValues[i] = true;
} else if ((strcasecmp(ptr, "FALSE") == 0) || (strcmp(ptr, "0") == 0)) {
config->boolValues[i] = false;
} else {
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;
@@ -133,29 +237,28 @@ void initialize_config(void)
/* No config file provided on command line */
if (!config_file) {
if (g_file_test(etc_config_file, G_FILE_TEST_IS_REGULAR)) {
if (access(etc_config_file, R_OK) == 0) {
config_file = etc_config_file;
} else {
if (g_file_test(default_config_file,
G_FILE_TEST_IS_REGULAR)) {
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)
@@ -175,17 +278,23 @@ 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]);
}
g_key_file_free(keyfile);
if (cmd_line_cfg) {
free(config_file);
}
}
/* Needed for unit testing */
void free_config_file(void)
{
if (config_file) {
free(config_file);
}
}
const char *server_addr_config()
{
initialize_config();
@@ -338,7 +447,19 @@ const char *rate_limit_strategy_config()
bool daemon_recycling_enabled_config(void)
{
initialize_config();
return config.boolValues[CONF_DAEMON_RECYCLING_ENABLED];
initialize_config();
return config.boolValues[CONF_DAEMON_RECYCLING_ENABLED];
}
bool record_retention_enabled_config(void)
{
initialize_config();
return config.boolValues[CONF_RECORD_RETENTION_ENABLED];
}
bool record_server_delivery_enabled_config(void)
{
initialize_config();
return config.boolValues[CONF_RECORD_SERVER_DELIVERY_ENABLED];
}
/* vi: set ts=8 sw=8 sts=4 et tw=80 cino=(0: */
+43 -8
View File
@@ -18,11 +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*/)
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,
@@ -32,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,
@@ -44,9 +63,10 @@ enum config_int_keys {
};
enum config_bool_keys {
CONF_BOOL_MIN = 0,
CONF_RATE_LIMIT_ENABLED,
CONF_DAEMON_RECYCLING_ENABLED,
CONF_RATE_LIMIT_ENABLED = 0,
CONF_DAEMON_RECYCLING_ENABLED,
CONF_RECORD_RETENTION_ENABLED,
CONF_RECORD_SERVER_DELIVERY_ENABLED,
CONF_BOOL_MAX
};
@@ -59,7 +79,16 @@ typedef struct configuration {
} configuration;
/* Sets the configuration file to be used later */
void set_config_file(char *filename);
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);
@@ -117,4 +146,10 @@ const char *get_tidheader_config(void);
/* Gets whether recycling is enabled */
bool daemon_recycling_enabled_config(void);
/* Gets whether local retention of records is enabled */
bool record_retention_enabled_config(void);
/* Gets whether records should be sent to server_addr */
bool record_server_delivery_enabled_config(void);
/* vi: set ts=8 sw=8 sts=4 et tw=80 cino=(0: */
+46
View File
@@ -0,0 +1,46 @@
[settings]
server=http://127.0.0.1
cainfo=/tmp/cacert.crt
tidheader=X-Telemetry-TID:\s6907c830-eed9-4ce9-81ae-76daf8d88f0f
socket_path=/tmp/test_telem_socket
#record expiry time in minutes
record_expiry=1200
spool_dir=/tmp/spool
#maximum size of the spool directory in KB
spool_max_size=1024
#time in seconds for processing spool
spool_process_time=900
#rate limiting record burst limit
record_burst_limit=100
#rate limiting record window length
record_window_length = 15
#rate limiting byte burst limit
byte_burst_limit = 1000
#rate limiting byte window length
byte_window_length=20
#rate limit enabled
rate_limit_enabled=true
#rate limit strategy if record not send
rate_limit_strategy=spool
#daemon recycling enabled
daemon_recycling_enabled=true
#server delivery disable
record_server_delivery_enabled=false
#local copy enabled
record_retention_enabled=true
+4
View File
@@ -0,0 +1,4 @@
[settings]
server=http://127.0.0.1
cainfo=/tmp/cacert.crt
+1 -1
View File
@@ -16,7 +16,7 @@ spool_dir=/tmp/spool
spool_max_size=1024
#time in seconds for processing spool
spool_process_time=900
spool_process_time=180
#rate limiting record burst limit
record_burst_limit=100
+4 -4
View File
@@ -1,11 +1,11 @@
[Unit]
Description=Telemetrics Heartbeat Probe
Requires=telemd.socket
After=telemd.socket
ConditionPathExists=!/etc/telemetrics/opt-out
Requires=telemprobd.socket
After=telemprobd.socket
ConditionPathExists=/etc/telemetrics/opt-in
[Service]
ExecStart=@bindir@/hprobe -H
ExecStart=@bindir@/hprobe -Hlu
User=telemetry
[Install]
+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]
+12
View File
@@ -0,0 +1,12 @@
[Unit]
Description=Telemetrics Systemd Journal Probe
Requires=telemprobd.socket
After=telemprobd.socket
ConditionPathExists=/etc/telemetrics/opt-in
[Service]
ExecStart=@bindir@/journalprobe -t
User=telemetry
[Install]
WantedBy=multi-user.target
+3 -3
View File
@@ -1,8 +1,8 @@
[Unit]
Description=Telemetrics Systemd Journal Probe
Requires=telemd.socket
After=telemd.socket
ConditionPathExists=!/etc/telemetrics/opt-out
Requires=telemprobd.socket
After=telemprobd.socket
ConditionPathExists=/etc/telemetrics/opt-in
[Service]
ExecStart=@bindir@/journalprobe
+3 -2
View File
@@ -1,7 +1,8 @@
[Unit]
Description=Telemetrics Kernel Log Scanner
Requires=telemd.socket
After=telemd.socket oops-probe.service
ConditionVirtualization=!container
Requires=telemprobd.socket
After=telemprobd.socket
[Service]
ExecStart=@bindir@/klogscanner
+33 -17
View File
@@ -4,23 +4,29 @@ pathfix = @sed \
-e 's|@libdir[@]|$(libdir)|g' \
-e 's|@localstatedir[@]|$(localstatedir)|g' \
-e 's|@PACKAGE_VERSION[@]|$(PACKAGE_VERSION)|g' \
-e 's|@SOCKETDIR[@]|$(SOCKETDIR)|g'
-e 's|@SOCKETDIR[@]|$(SOCKETDIR)|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%/journal-probe.service.in \
%D%/oops-probe.service.in \
%D%/journal-probe-tail.service.in \
%D%/pstore-probe.service.in \
%D%/klogscanner.service.in \
%D%/pstore-clean.service.in \
%D%/libtelemetry.pc.in \
%D%/telemd.path.in \
%D%/telemd.service.in \
%D%/telemd.socket.in \
%D%/telempostd.service.in \
%D%/telempostd.path.in \
%D%/telemprobd.service.in \
%D%/telemprobd.socket.in \
%D%/telemprobd-update-trigger.service.in \
%D%/telemetrics-dirs.conf.in \
%D%/telemetrics.conf.in
@@ -47,13 +53,15 @@ systemdunit_DATA = \
%D%/hprobe.service \
%D%/hprobe.timer \
%D%/journal-probe.service \
%D%/oops-probe.service \
%D%/journal-probe-tail.service \
%D%/pstore-probe.service \
%D%/klogscanner.service \
%D%/pstore-clean.service \
%D%/telemd.service \
%D%/telemd.socket \
%D%/telemd.path
%D%/telemprobd.service \
%D%/telemprobd.socket \
%D%/telemprobd-update-trigger.service \
%D%/telempostd.service \
%D%/telempostd.path
%D%/hprobe.service: %D%/hprobe.service.in
$(pathfix) < $< > $@
@@ -61,7 +69,7 @@ systemdunit_DATA = \
%D%/journal-probe.service: %D%/journal-probe.service.in
$(pathfix) < $< > $@
%D%/oops-probe.service: %D%/oops-probe.service.in
%D%/journal-probe-tail.service: %D%/journal-probe-tail.service.in
$(pathfix) < $< > $@
%D%/pstore-probe.service: %D%/pstore-probe.service.in
@@ -73,13 +81,19 @@ systemdunit_DATA = \
%D%/pstore-clean.service: %D%/pstore-clean.service.in
$(pathfix) < $< > $@
%D%/telemd.service: %D%/telemd.service.in
%D%/telempostd.path: %D%/telempostd.path.in
$(pathfix) < $< > $@
%D%/telemd.socket: %D%/telemd.socket.in
%D%/telempostd.service: %D%/telempostd.service.in
$(pathfix) < $< > $@
%D%/telemd.path: %D%/telemd.path.in
%D%/telemprobd.service: %D%/telemprobd.service.in
$(pathfix) < $< > $@
%D%/telemprobd.socket: %D%/telemprobd.socket.in
$(pathfix) < $< > $@
%D%/telemprobd-update-trigger.service: %D%/telemprobd-update-trigger.service.in
$(pathfix) < $< > $@
sysctldir = @SYSTEMD_SYSCTLDIR@
@@ -92,15 +106,17 @@ systemconfdir = @SYSTEMD_SYSTEMCONFDIR@
systemconf_DATA = %D%/40-core-ulimit.conf
clean-local:
-rm -f %D%/telemd.service \
%D%/telemd.socket \
%D%/telemd.path \
-rm -f %D%/telemprobd.service \
%D%/telemprobd.socket \
%D%/telempostd.service \
%D%/telempostd.path \
%D%/telemprobd-update-trigger.service \
%D%/telemetrics.conf \
%D%/telemetrics-dirs.conf \
%D%/libtelemetry.pc \
%D%/40-crash-probe.conf \
%D%/journal-probe.service \
%D%/oops-probe.service \
%D%/journal-probe-tail.service \
%D%/pstore-probe.service \
%D%/klogscanner.service \
%D%/pstore-clean.service \
-11
View File
@@ -1,11 +0,0 @@
[Unit]
Description=Telemetrics Kernel Oops Probe
Requires=telemd.socket
After=telemd.socket
[Service]
ExecStart=@bindir@/oopsprobe
User=telemetry
[Install]
WantedBy=multi-user.target
+3 -2
View File
@@ -1,7 +1,8 @@
[Unit]
Description=Telemetrics Pstore Probe
Requires=telemd.socket
After=telemd.socket
Requires=telemprobd.socket
After=telemprobd.socket
ConditionPathExists=/etc/telemetrics/opt-in
[Service]
ExecStart=@bindir@/pstoreprobe
+2 -1
View File
@@ -1,5 +1,6 @@
d @localstatedir@/lib/telemetry 0755 telemetry telemetry -
d @localstatedir@/spool/telemetry 0750 telemetry telemetry -
d @localstatedir@/log/telemetry 0750 telemetry telemetry -
d @localstatedir@/log/telemetry/records 0750 telemetry telemetry -
d @localstatedir@/cache/telemetry 0750 telemetry telemetry -
d @localstatedir@/cache/telemetry/oops 0750 telemetry telemetry -
d @localstatedir@/cache/telemetry/pstore 0750 telemetry telemetry -
+28 -16
View File
@@ -1,57 +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..3600. Values outside this range are clamped.
spool_process_time=900
# Valid range: 120..300. Values outside this range are clamped.
#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 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
@@ -1,6 +1,6 @@
[Unit]
Description=Telemetry Daemon Spool
ConditionPathExists=!/etc/telemetrics/opt-out
Description=Telemetry Post Daemon staging
ConditionPathExists=/etc/telemetrics/opt-in
[Path]
DirectoryNotEmpty=@localstatedir@/spool/telemetry
+12
View File
@@ -0,0 +1,12 @@
[Unit]
Description=Telemetrics Post Daemon
ConditionPathExists=/etc/telemetrics/opt-in
[Service]
ExecStart=@bindir@/telempostd
WorkingDirectory=@localstatedir@/spool/telemetry
User=telemetry
[Install]
Also=telempostd.path
WantedBy=multi-user.target
@@ -0,0 +1,7 @@
[Unit]
Description=Restart Telemetrics Daemon On Update
BindsTo=update-triggers.target
[Service]
Type=oneshot
ExecStart=@systemctldir@/systemctl try-restart telemprobd.service
@@ -1,12 +1,12 @@
[Unit]
Description=Telemetrics Daemon
ConditionPathExists=!/etc/telemetrics/opt-out
ConditionPathExists=/etc/telemetrics/opt-in
[Service]
ExecStart=@bindir@/telemd
ExecStart=@bindir@/telemprobd
WorkingDirectory=@localstatedir@/spool/telemetry
User=telemetry
[Install]
Also=telemd.socket telemd.path
Also=telemprobd.socket
WantedBy=multi-user.target
@@ -1,6 +1,6 @@
[Unit]
Description=Telemetrics Daemon
ConditionPathExists=!/etc/telemetrics/opt-out
ConditionPathExists=/etc/telemetrics/opt-in
[Socket]
ListenStream=@SOCKETDIR@/telem-0
+143
View File
@@ -0,0 +1,143 @@
/*
* This program is part of the Clear Linux Project
*
* Copyright 2018 Intel Corporation
*
* This program is free software; you can redistribute it and/or modify it under
* the terms and conditions of the GNU Lesser General Public License, as
* published by the Free Software Foundation; either version 2.1 of the License,
* or (at your option) any later version.
*
* This program is distributed in the hope it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*/
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <limits.h>
#include <stdbool.h>
#include <sys/stat.h>
#include "log.h"
#include "util.h"
#include "common.h"
#include "iorecord.h"
bool _fgets(char *s, int n, FILE *stream)
{
char *i;
if (!fgets(s, n, stream)) {
return false;
}
if ((i = strchr(s, '\n')) != NULL) {
*i = '\0';
}
return true;
}
bool read_record(char *fullpath, char *headers[], char **body, char **cfg_file)
{
int i = 0;
bool result = false;
FILE *fp = NULL;
long offset;
#if (LINE_MAX > PATH_MAX)
char line[LINE_MAX+1] = { 0 };
#else
char line[PATH_MAX+1] = { 0 };
#endif
long size;
uint32_t cfg_prefix = 0;
fp = fopen(fullpath, "r");
if (fp == NULL) {
telem_log(LOG_ERR, "Unable to open file %s in staging\n", fullpath);
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, sizeof(line), fp)) {
telem_log(LOG_ERR, "Error while parsing staged record\n");
fclose(fp);
return false;
}
//Get rid of trailing newline
result = get_header(line, header_name, &headers[i]);
if (!result) {
telem_log(LOG_ERR, "read_record: Incorrect"
" headers in record\n");
goto read_error;
}
}
offset = ftell(fp);
if (offset == -1) {
telem_perror("Failed to get staged file offset of record payload");
result = false;
goto read_error;
}
size = size - offset + 1;
*body = malloc((size_t)size);
if (*body == NULL) {
telem_log(LOG_ERR, "Could not allocate memory for payload from staged file\n");
result = false;
goto read_error;
}
memset(*body, 0, (size_t)size);
//read rest of file. '-1' ensures it's null terminated
size_t newlen = fread(*body, sizeof(char), (size_t)size - 1, fp);
if (newlen == 0) {
telem_perror("Error reading staged file");
result = false;
goto read_error;
} else {
result = true;
}
read_error:
if (fp) {
fclose(fp);
}
return result;
}
+28
View File
@@ -0,0 +1,28 @@
/*
* This program is part of the Clear Linux Project
*
* Copyright 2018 Intel Corporation
*
* This program is free software; you can redistribute it and/or modify it under
* the terms and conditions of the GNU Lesser General Public License, as
* published by the Free Software Foundation; either version 2.1 of the License,
* or (at your option) any later version.
*
* This program is distributed in the hope it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*/
#include <stdbool.h>
/**
* Reads a telemetry record
*
* @param fullpath pointer to full path file name
* @param headers pointer to array of headers and values
* @param body record message content
*
* @return true if successful otherwise false
*/
bool read_record(char *fullpath, char *headers[], char **body, char **cfg);
+115
View File
@@ -0,0 +1,115 @@
/*
* This program is part of the Clear Linux Project
*
* Copyright 2018 Intel Corporation
*
* This program is free software; you can redistribute it and/or modify it under
* the terms and conditions of the GNU Lesser General Public License, as
* published by the Free Software Foundation; either version 2.1 of the License,
* or (at your option) any later version.
*
* This program is distributed in the hope it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*/
#define _GNU_SOURCE
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <getopt.h>
#include "common.h"
#include "journal.h"
static void print_usage(void)
{
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 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");
}
int main(int argc, char **argv)
{
int rc = EXIT_SUCCESS;
int count = 0;
bool verbose_output = false;
bool record = false;
char *boot_id = NULL;
char *record_id = NULL;
char *event_id = NULL;
char *classification = NULL;
struct TelemJournal *telem_journal = NULL;
/** opts */
int c;
int opt_index = 0;
struct option opts[] = {
{ "record_id", 1, NULL, 'r' },
{ "event_id", 1, NULL, 'e' },
{ "classification", 1, NULL, 'c' },
{ "boot_id", 1, NULL, 'b' },
{ "verbose", 0, NULL, 'V' },
{ "include_record", 0, NULL, 'i' },
{ "help", 0, NULL, 'h' },
{ NULL, 0, NULL, 0 }
};
while ((c = getopt_long(argc, argv, "r:e:c:b:Vih", opts, &opt_index)) != -1) {
switch (c) {
case 'r':
record_id = optarg;
break;
case 'e':
event_id = optarg;
break;
case 'c':
classification = optarg;
break;
case 'b':
boot_id = optarg;
break;
case 'V':
verbose_output = true;
break;
case 'i':
record = true;
break;
case 'h':
print_usage();
exit(EXIT_SUCCESS);
default:
print_usage();
exit(EXIT_FAILURE);
}
}
if ((telem_journal = open_journal(JOURNAL_PATH))) {
if (verbose_output) {
fprintf(stdout, "%-30s %-27s %-32s %-32s %-36s\n", "Classification", "Time stamp",
"Record ID", "Event ID", "Boot ID");
}
count = print_journal(telem_journal, classification, record_id, event_id, boot_id, record);
if (verbose_output) {
fprintf(stdout, "Total records: %d\n", count);
}
close_journal(telem_journal);
} else {
fprintf(stderr, "Unable to open journal\n");
rc = EXIT_FAILURE;
}
return rc;
}
+680
View File
@@ -0,0 +1,680 @@
/*
* This program is part of the Clear Linux Project
*
* Copyright 2018 Intel Corporation
*
* This program is free software; you can redistribute it and/or modify it under
* the terms and conditions of the GNU Lesser General Public License, as
* published by the Free Software Foundation; either version 2.1 of the License,
* or (at your option) any later version.
*
* This program is distributed in the hope it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*/
#define _GNU_SOURCE
#define ID_LEN 32
#define BOOTID_LEN 37 // Includes the \n character at the end
#define BOOTID_FILE "/proc/sys/kernel/random/boot_id"
#define MID_BUFF 1024
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <inttypes.h>
#include <sys/uio.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <assert.h>
#include "log.h"
#include "util.h"
#include "common.h"
#include "journal.h"
/*
* Packs journal entry values into one line string separated
* by '\036' RS.
*
* @param entry A pointer to a JournalEntry struct with values
* to serialize.
* @param buff A pointer to be initialized and set with the
* serialized values.
*
* @return 0 on success, -1 on failure.
*/
static int serialize_journal_entry(struct JournalEntry *entry, char **buff)
{
int rc = 0;
if (entry == NULL) {
return -1;
}
if (asprintf(buff, "%s\036%lld\036%s\036%s\036%s", entry->record_id, (long long int)entry->timestamp,
entry->classification, entry->event_id, entry->boot_id) < 0) {
telem_log(LOG_ERR, "Error: Unable to serialize data in buff\n");
rc = -1;
}
return rc;
}
/**
* Frees journal entry struct members and journal entry pointer.
*
* @param the pointer to the entry structure to free.
*/
static void free_journal_entry(struct JournalEntry *entry)
{
if (entry) {
free(entry->classification);
free(entry->record_id);
free(entry->event_id);
free(entry->boot_id);
free(entry);
}
}
/**
* Unpacks a record that was stored in file and initializes a
* JournalEntry struct with parsed values.
*
* @param line A pointer to the data to unpack.
* @param entry A pointer to be initialized and set with values
* parsed from parameter line.
*
* @return 0 on success, -1 on failure.
*/
static int deserialize_journal_entry(char *line, struct JournalEntry **entry)
{
int rc = -1;
size_t len = 0;
size_t llen = 0;
size_t offset = 0;
struct JournalEntry *e = NULL;
/* Assume an error... */
*entry = NULL;
if (line == NULL || !strlen(line)) {
return -1;
}
e = calloc(1, sizeof(struct JournalEntry));
if (!e) {
return -1;
}
llen = strlen(line);
for (int i = 0; i < 5; i++) {
char *out = NULL;
if (offset > llen) {
rc = -1;
break;
}
len = strcspn(line + offset, "\036\n");
out = strndup((line + offset), len);
offset += len + 1;
if (out) {
switch (i) {
case 0:
e->record_id = out;
break;
case 1:
e->timestamp = atoll(out);
free(out);
break;
case 2:
e->classification = out;
break;
case 3:
e->event_id = out;
break;
case 4:
e->boot_id = out;
rc = 0;
break;
}
} else {
rc = -1;
break;
}
}
if (rc == 0) {
*entry = e;
} else {
free_journal_entry(e);
}
return rc;
}
/**
* Counts the number of records in journal.
*
* @param telem_journal Pointer to a telemetry journal
* to get record count.
*
* @return record count
*
*/
static int get_record_count(struct TelemJournal *telem_journal)
{
int count = 0;
size_t len;
ssize_t read;
char *line = NULL;
rewind(telem_journal->fptr);
// read until the end line by line
while ((read = getline(&line, &len, telem_journal->fptr)) != -1) {
count++;
}
free(line);
return count;
}
/**
* Serializes JournalEntry and saves it to file.
*
* @param fptr A FILE pointer.
* @param entry A pointer to data.
*
* @return 0 on succes, !=0 on failure
*/
static int save_entry(FILE *fptr, struct JournalEntry *entry)
{
int rc = -1;
char *serialized_data = NULL;
if (fptr == NULL || entry == NULL) {
return rc;
}
if ((rc = serialize_journal_entry(entry, &serialized_data)) == 0) {
telem_debug("DEBUG: Saving: %s\n", serialized_data);
fprintf(fptr, "%s\n", serialized_data);
fflush(fptr);
free(serialized_data);
}
return rc;
}
/**
* Reads the boot unique identifier from BOOTID_FILE
*
* @param buff pointer to a BOOTID_LEN allocated
*
* @return 0 on success, -1 on failure
*/
static int read_boot_id(char buff[])
{
int rc = -1;
FILE *fs = NULL;
fs = fopen(BOOTID_FILE, "r");
if (!fs) {
telem_log(LOG_ERR, "Error: Unable to open %s for reading: %d\n", BOOTID_FILE, errno);
return rc;
}
if (fgets(buff, BOOTID_LEN, fs)) {
rc = 0;
}
fclose(fs);
return rc;
}
/**
* Moves the file position indicator n lines.
*
* @param n Number of lines to skip.
* @param fptr A file pointer.
* @param found_record A pointer to a function to be called
* every time a valid record is found, if
* pointer is not set to null.
*
* @returns 0 on success, on failure -1 if n > existing
* lines in file or errno if failure happens
* during file operation.
*/
static int skip_n_lines(int n, FILE *fptr, int (*found_record)(char *))
{
int rc = 0;
size_t len = 0;
char *line = NULL;
struct JournalEntry *entry = NULL;
if (fptr == NULL) {
telem_log(LOG_ERR, "fptr argument to skip_n_lines is invalid\n");
// File descriptor in bad state
return EBADFD;
}
if (fseek(fptr, 0, 0) != 0) {
return errno;
}
for (int i = 0; i < n; i++) {
if (getline(&line, &len, fptr) == -1) {
rc = -1;
break;
}
if (found_record != NULL) {
if (!strlen(line))
continue;
deserialize_journal_entry(line, &entry);
if (entry) {
found_record(entry->record_id);
free_journal_entry(entry);
entry = NULL;
}
}
}
free(line);
return rc;
}
/**
* Copies contents of fptr to a known location.
*
* @param fptr A file pointer from source file.
*
* @returns 0 on success, errno on failure
*/
static int copy_to_tmp(FILE *fptr, char *tmp_path)
{
int rc = 0;
size_t len;
char *line = NULL;
FILE *fptr_tmp = NULL;
if (fptr == NULL) {
return -1;
}
fptr_tmp = fopen(tmp_path, "w");
if (!fptr_tmp) {
return errno;
}
// Copy lines to new file
while (getline(&line, &len, fptr) > -1) {
if (fprintf(fptr_tmp, "%s", line) < 0) {
rc = errno;
break;
}
}
if (fclose(fptr_tmp) != 0) {
rc = errno;
telem_perror("Error");
}
free(line);
return rc;
}
/* Exported function */
TelemJournal *open_journal(const char *journal_file)
{
FILE *fptr = NULL;
char boot_id[BOOTID_LEN] = { '\0' };
struct TelemJournal *telem_journal;
// Use default location if journal_file parameter is NULL
if (journal_file == NULL) {
fptr = fopen(JOURNAL_PATH, "a+");
} else {
fptr = fopen(journal_file, "a+");
}
if (fptr == NULL) {
telem_perror("Error while opening journal file");
return NULL;
}
if (read_boot_id(boot_id) != 0) {
telem_perror("Error while reading boot_id");
fclose(fptr);
return NULL;
}
telem_journal = malloc(sizeof(struct TelemJournal));
if (!telem_journal) {
telem_log(LOG_CRIT, "CRIT: Unable to allocate memory\n");
fclose(fptr);
return NULL;
}
telem_journal->fptr = fptr;
telem_journal->journal_file = \
(journal_file == NULL) ? strdup(JOURNAL_PATH) : strdup(journal_file);
/* boot_id includes \n at the end, strip RC during duplication */
telem_journal->boot_id = strndup(boot_id, BOOTID_LEN - 1);
telem_journal->record_count = get_record_count(telem_journal);
telem_journal->record_count_limit = RECORD_LIMIT;
telem_journal->latest_record_id = NULL;
telem_journal->prune_entry_callback = NULL;
telem_debug("Records in db: %d\n", telem_journal->record_count);
return telem_journal;
}
/* Exported function */
void close_journal(TelemJournal *telem_journal)
{
if (telem_journal) {
free(telem_journal->boot_id);
free(telem_journal->journal_file);
free(telem_journal->latest_record_id);
fclose(telem_journal->fptr);
free(telem_journal);
}
}
/**
* Checks if class is a prefix i.e. t/\* or t/t/\*
*
* @param class A pointer to string with classification
* value to check.
* @return 1 if class is a prefix, 0 otherwise
*/
static int is_class_prefix(char *class)
{
size_t class_len = strlen(class);
return (strcmp((char *)(class + class_len - 2), "/*") == 0) ? 1 : 0;
}
/**
* Print records content
*
* @param record_id Unique record identifier
*
*/
static void print_record(char *record_id)
{
int rc = 0;
size_t read = 0;
char buff[MID_BUFF] = { '\0' };
char *filepath = NULL;
FILE *recordfp = NULL;
rc = asprintf(&filepath, "%s/%s", RECORD_RETENTION_DIR, record_id);
if (rc == -1) {
// just bail out, there are worse problems
return;
}
recordfp = fopen(filepath, "r");
if (!recordfp) {
telem_log(LOG_INFO, "Could not open record %s: %s\n", record_id, strerror(errno));
free(filepath);
return;
}
while ((read = fread(buff, 1, sizeof(buff), recordfp)) > 0) {
fwrite(buff, 1, read, stdout);
}
free(filepath);
fclose(recordfp);
}
/* Exported function */
int print_journal(TelemJournal *telem_journal, char *classification,
char *record_id, char *event_id, char *boot_id,
bool include_record)
{
int n = 0;
int rc = 0;
int count = 0;
char str_time[80] = { '\0' };
char *line = NULL;
size_t len = 0;
FILE *journal_fileptr = NULL;
struct tm ts;
struct JournalEntry *entry = NULL;
if (telem_journal == NULL) {
return -1;
}
journal_fileptr = fopen(telem_journal->journal_file, "r");
if (!journal_fileptr) {
return -1;
}
// Advance file pointer to line n
n = telem_journal->record_count - telem_journal->record_count_limit;
if (n > 0) {
rc = skip_n_lines(n, journal_fileptr, NULL);
if (rc == -1) {
return rc;
} else if (rc != 0) {
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 */
if (record_id != NULL && strcmp(entry->record_id, record_id) != 0) {
goto skip_print;
}
if (boot_id != NULL && strcmp(entry->boot_id, boot_id) != 0) {
goto skip_print;
}
if (event_id != NULL && strcmp(entry->event_id, event_id) != 0) {
goto skip_print;
}
// In the case of class checking prefixes is an option
if (classification != NULL) {
// Check prefixes when classification ends in /*, otherwise use strcomp
if (is_class_prefix(classification)) {
if (strncmp(entry->classification, classification, strlen(classification) - 1) != 0) {
goto skip_print;
}
} else if (strcmp(entry->classification, classification) != 0) {
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) {
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);
/* print record content */
if (include_record) {
print_record(entry->record_id);
}
count++;
skip_print:
free_journal_entry(entry);
}
}
free(line);
fclose(journal_fileptr);
return count;
}
/**
* Validation for event id. Thic check makes sure
* that an event_id is a 32 hexadecimal chars long.
*
* @param event_id A pointer to id value.
*
* @return 0 on success, 1 on failure
*/
static int validate_event_id(char *event_id)
{
const char alphab[] = EVENT_ID_ALPHAB;
if (event_id == NULL) {
return 1;
}
if (strlen(event_id) != EVENT_ID_LEN) {
return 1;
}
if (strspn(event_id, alphab) != EVENT_ID_LEN) {
return 1;
}
return 0;
}
/* Exported function */
int new_journal_entry(TelemJournal *telem_journal, char *classification,
time_t timestamp, char *event_id)
{
int rc = 1;
char boot_id[BOOTID_LEN] = { '\0' };
char *record_id = NULL;
struct JournalEntry *entry = NULL;
if (telem_journal == NULL) {
telem_log(LOG_ERR, "telem_journal was not initialized\n");
return rc;
}
if (validate_classification(classification) != 0) {
return rc;
}
if (validate_event_id(event_id) != 0) {
return rc;
}
entry = malloc(sizeof(struct JournalEntry));
if (!entry) {
telem_log(LOG_CRIT, "CRIT: unable to allocate memory\n");
return rc;
}
entry->classification = NULL;
entry->record_id = NULL;
entry->event_id = NULL;
entry->boot_id = NULL;
if (get_random_id(&record_id) != 0) {
telem_log(LOG_ERR, "Erorr: Unable to generate random id\n");
goto quit;
}
if (read_boot_id(boot_id) != 0) {
telem_log(LOG_ERR, "Error: Unable to read boot_id\n");
goto quit;
}
if ((entry->classification = strdup(classification)) == NULL) {
goto quit;
}
if ((entry->event_id = strdup(event_id)) == NULL) {
goto quit;
}
entry->record_id = record_id;
/* boot_id includes \n at the end, strip RC during duplication */
entry->boot_id = strndup(boot_id, BOOTID_LEN - 1);
entry->timestamp = timestamp;
if ((rc = save_entry(telem_journal->fptr, entry)) == 0) {
telem_journal->record_count = telem_journal->record_count + 1;
telem_debug("DEBUG: %d records in journal\n", telem_journal->record_count);
}
free(telem_journal->latest_record_id);
telem_journal->latest_record_id = strdup(entry->record_id);
quit:
free_journal_entry(entry);
return rc;
}
/* Exported function */
int prune_journal(struct TelemJournal *telem_journal, char *tmp_dir)
{
int rc = 1;
int count = 0;
int deviation = DEVIATION;
char *tmp_file_path = NULL;
if (telem_journal == NULL) {
return rc;
}
if (telem_journal->record_count > (deviation + telem_journal->record_count_limit)) {
count = telem_journal->record_count - telem_journal->record_count_limit;
// jump to line# count
if ((rc = skip_n_lines(count, telem_journal->fptr, telem_journal->prune_entry_callback)) != 0) {
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;
}
// Use default if not tmp_dir is specified
if (tmp_dir == NULL) {
if ((asprintf(&tmp_file_path, "%s/.journal", JOURNAL_TMPDIR) == -1)) {
return ENOMEM;
}
} else {
if ((asprintf(&tmp_file_path, "%s/.journal", tmp_dir) == -1)) {
return ENOMEM;
}
}
// create new file with rest of file
if ((rc = copy_to_tmp(telem_journal->fptr, tmp_file_path)) != 0) {
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) {
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) {
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) {
telem_log(LOG_ERR, "Error re-opening journal file\n");
rc = 1;
goto quit;
}
// update record count
telem_journal->record_count = telem_journal->record_count - count;
telem_debug("DEBUG: record_count: %d\n", telem_journal->record_count);
}
rc = 0;
quit:
free(tmp_file_path);
return rc;
}
/* vi: set ts=8 sw=8 sts=4 et tw=80 cino=(0: */
+117
View File
@@ -0,0 +1,117 @@
/*
* This program is part of the Clear Linux Project
*
* Copyright 2018 Intel Corporation
*
* This program is free software; you can redistribute it and/or modify it under
* the terms and conditions of the GNU Lesser General Public License, as
* published by the Free Software Foundation; either version 2.1 of the License,
* or (at your option) any later version.
*
* This program is distributed in the hope it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*/
#define _GNU_SOURCE
/* default record limit */
#define RECORD_LIMIT 100
#define DEVIATION 50
#include <time.h>
#include <stdio.h>
#include <stdbool.h>
/* Journal entry type */
typedef struct JournalEntry {
time_t timestamp;
char *classification;
char *record_id;
char *event_id;
char *boot_id;
} JournalEntry;
/* Telemetry journal type */
typedef struct TelemJournal {
FILE *fptr;
char *journal_file;
char *boot_id;
char *latest_record_id;
int record_count;
int record_count_limit;
int (*prune_entry_callback)(char *);
} TelemJournal;
/**
* Telemetry journal initialization.
*
* @param journal_file A pointer to a string containing
* the full path to file used as journal storage.
* If journal_file is set to NULL a default value
* will be used.
*
* @returns a telemetry journal structure in success or
* NULL in case of failure.
*/
TelemJournal *open_journal(const char *journal_file);
/**
* Closes journal file and deallocates memory that was
* previously initialized by open_journal call.
*
* @param telem_journal A pointer to struct that has a
* reference to journal file pointer and other
* members.
*/
void close_journal(TelemJournal *telem_journal);
/**
* Prints journal contents to stdout. Use function parameters
* to filter journal entries to print.
*
* @param telem_journal A pointer to struct initialized
* by open_journal call.
* @param classification A pointer to string used as
* classification filter.
* @param record_id A pointer to string used as record_id filter.
* @param event_id A pointer to string used as event_id filter.
* @param boot_id A pointer to string used as boor_id filter.
* @param include_record A flag to control record content print.
*
* @return the number of lines printed to stdout on success, -1
* on failure.
*/
int print_journal(TelemJournal *telem_journal, char *classification,
char *record_id, char *event_id, char *boot_id,
bool include_record);
/**
* Creates a new entry in journal.
*
* @param telem_journal A pointer to telemetry journal.
* @param classification A pointer to a classification value.
* @param timestamp Record creation time stamp value.
* @param event_id A pointer to a unique id value for record
* event.
*
* @return 0 on success 1 on failure.
*/
int new_journal_entry(TelemJournal *telem_journal, char *classification,
time_t timestamp, char *event_id);
/**
* Checks number of lines in log and prunes the oldest records
* if journal grows more than telem_journal->record_count_limit.
*
* @param telem_journal A pointer to telemetry journal struct
* returned by open_journal call.
* @param tmp_dir A pointer to a string with the path to use for
* temp file during pruning process.
*
* @return 0 on success, errno on failure
*/
int prune_journal(TelemJournal *telem_journal, char *tmp_dir);
/* vi: set ts=8 sw=8 sts=4 et tw=80 cino=(0: */
+15
View File
@@ -0,0 +1,15 @@
bin_PROGRAMS += \
%D%/telem_journal
%C%_telem_journal_SOURCES = %D%/cli.c \
%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
+81 -30
View File
@@ -1,41 +1,96 @@
bin_PROGRAMS = %D%/telemd
bin_PROGRAMS = \
%D%/telemprobd \
%D%/telempostd \
%D%/telemctl
%C%_telemd_SOURCES = \
%D%/main.c \
%D%/telemdaemon.c \
%D%/telemdaemon.h \
%D%/spool.c \
%D%/spool.h
%C%_telemctl_SOURCES = \
%D%/telemctl.c
%C%_telemd_LDADD = $(CURL_LIBS) \
%D%/libtelem-shared.la
%C%_telemd_CFLAGS = \
%C%_telemctl_CFLAGS = \
$(AM_CFLAGS)
%C%_telemd_LDFLAGS = \
%C%_telemctl_LDFLAGS = \
$(AM_LDFLAGS) \
-pie
%C%_telemprobd_SOURCES = \
%D%/probe.c \
%D%/telemdaemon.c \
%D%/telemdaemon.h \
%D%/journal/journal.c \
%D%/journal/journal.h
%C%_telemprobd_LDADD = $(CURL_LIBS) \
%D%/libtelem-shared.la \
%D%/libtelemetry.la
%C%_telemprobd_CFLAGS = \
$(AM_CFLAGS)
%C%_telemprobd_LDFLAGS = \
$(AM_LDFLAGS) \
-pie
if HAVE_SYSTEMD_DAEMON
%C%_telemd_CFLAGS += \
$(SYSTEMD_DAEMON_CFLAGS)
%C%_telemd_LDADD += \
$(SYSTEMD_DAEMON_LIBS)
%C%_telemprobd_CFLAGS += \
$(SYSTEMD_DAEMON_CFLAGS)
%C%_telemprobd_LDADD += \
$(SYSTEMD_DAEMON_LIBS)
endif
if LOG_SYSTEMD
if HAVE_SYSTEMD_JOURNAL
%C%_telemd_CFLAGS += \
$(SYSTEMD_JOURNAL_CFLAGS)
%C%_telemd_LDADD += \
$(SYSTEMD_JOURNAL_LIBS)
%C%_telemprobd_CFLAGS += \
$(SYSTEMD_JOURNAL_CFLAGS)
%C%_telemprobd_LDADD += \
$(SYSTEMD_JOURNAL_LIBS)
endif
endif
# Telemetry post daemon
%C%_telempostd_SOURCES = \
%D%/post.c \
%D%/telempostdaemon.c \
%D%/telempostdaemon.h \
%D%/journal/journal.c \
%D%/journal/journal.h \
%D%/spool.h \
%D%/spool.c \
%D%/retention.h \
%D%/retention.c \
%D%/iorecord.c \
%D%/iorecord.h
%C%_telempostd_LDADD = $(CURL_LIBS) $(JSON_C_LIBS) \
%D%/libtelem-shared.la \
%D%/libtelemetry.la
%C%_telempostd_CFLAGS = \
$(AM_CFLAGS)
%C%_telempostd_LDFLAGS = \
$(AM_LDFLAGS) \
-pie
if HAVE_SYSTEMD_DAEMON
%C%_telempostd_CFLAGS += \
$(SYSTEMD_DAEMON_CFLAGS)
%C%_telempostd_LDADD += \
$(SYSTEMD_DAEMON_LIBS)
endif
if LOG_SYSTEMD
if HAVE_SYSTEMD_JOURNAL
%C%_telempostd_CFLAGS += \
$(SYSTEMD_JOURNAL_CFLAGS)
%C%_telempostd_LDADD += \
$(SYSTEMD_JOURNAL_LIBS)
endif
endif
# set library version info
SHAREDLIB_CURRENT=3
SHAREDLIB_REVISION=0
SHAREDLIB_CURRENT=4
SHAREDLIB_REVISION=1
SHAREDLIB_AGE=0
noinst_LTLIBRARIES = %D%/libtelem-shared.la
@@ -44,20 +99,18 @@ noinst_LTLIBRARIES = %D%/libtelem-shared.la
%D%/util.c \
%D%/util.h \
%D%/configuration.c \
%D%/nica/inifile.c \
%D%/nica/hashmap.c \
%D%/configuration.h \
%D%/common.c \
%D%/common.h
%C%_libtelem_shared_la_CFLAGS = \
$(AM_CFLAGS) \
$(GLIB_CFLAGS)
$(AM_CFLAGS)
%C%_libtelem_shared_la_LDFLAGS = \
$(AM_LDFLAGS)
%C%_libtelem_shared_la_LIBADD = \
$(GLIB_LIBS)
lib_LTLIBRARIES = \
%D%/libtelemetry.la
@@ -69,12 +122,10 @@ include_HEADERS = %D%/telemetry.h
noinst_HEADERS = %D%/log.h
%C%_libtelemetry_la_CFLAGS = \
$(AM_CFLAGS) \
$(GLIB_CFLAGS)
$(AM_CFLAGS)
%C%_libtelemetry_la_LDFLAGS = \
$(AM_LDFLAGS) \
$(GLIB_LIBS) \
-version-info $(SHAREDLIB_CURRENT):$(SHAREDLIB_REVISION):$(SHAREDLIB_AGE) \
-Wl,--version-script=$(top_srcdir)/src/telemetry.sym
+5 -5
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);
@@ -81,7 +81,7 @@
#ifdef TM_LOG_STDERR
#define telem_log(priority, ...) do { \
if (priority <= MAX_LOG_LEVEL) { \
fprintf(stderr, __VA_ARGS__ ); \
fprintf(stderr, __VA_ARGS__); \
} \
} while (0);
#endif
+469
View File
@@ -0,0 +1,469 @@
/*
* This file is part of libnica.
*
* Copyright © 2016-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 <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "hashmap.h"
#define INITIAL_SIZE 61
/* When we're "full", 70% */
#define FULL_FACTOR 0.7
/* Multiple to increase bucket size by */
#define INCREASE_FACTOR 4
/**
* An bucket/chain within the hashmap
*/
typedef struct NcHashmapEntry {
void *hash; /**<The key for this item */
void *value; /**<Value for this item */
struct NcHashmapEntry *next; /**<Next item in the chain */
bool occ; /**<Whether this bucket is occupied */
} NcHashmapEntry;
/**
* A NcHashmap
*/
struct NcHashmap {
int size; /**<Current size of the hashmap */
int next_size; /**<Next size at which we need to resize */
int n_buckets; /**<Current number of buckets */
NcHashmapEntry *buckets; /**<Stores our bucket chains */
nc_hash_create_func hash; /**<Hash generation function */
nc_hash_compare_func compare; /**<Key comparison function */
nc_hash_free_func key_free; /**<Cleanup function for keys */
nc_hash_free_func value_free; /**<Cleanup function for values */
};
/**
* Iteration object
*/
typedef struct _NcHashmapIter {
int bucket; /**<Current bucket position */
NcHashmap *map; /**<Associated NcHashmap */
void *item; /**<Current item in this iteration */
} _NcHashmapIter;
static void nc_hashmap_update_next_size(NcHashmap *self);
static bool nc_hashmap_resize(NcHashmap *self);
static inline bool nc_hashmap_maybe_resize(NcHashmap *self)
{
if (!self) {
return false;
}
if (self->size >= self->next_size) {
return true;
}
return false;
}
static NcHashmap *nc_hashmap_new_internal(nc_hash_create_func create, nc_hash_compare_func compare,
nc_hash_free_func key_free, nc_hash_free_func value_free)
{
NcHashmap *map = NULL;
NcHashmapEntry *buckets = NULL;
map = calloc(1, sizeof(NcHashmap));
if (!map) {
return NULL;
}
buckets = calloc(INITIAL_SIZE, sizeof(NcHashmapEntry));
if (!buckets) {
free(map);
return NULL;
}
map->buckets = buckets;
map->n_buckets = INITIAL_SIZE;
map->hash = create ? create : nc_simple_hash;
map->compare = compare ? compare : nc_simple_compare;
map->key_free = key_free;
map->value_free = value_free;
map->size = 0;
nc_hashmap_update_next_size(map);
return map;
}
NcHashmap *nc_hashmap_new(nc_hash_create_func create, nc_hash_compare_func compare)
{
return nc_hashmap_new_internal(create, compare, NULL, NULL);
}
NcHashmap *nc_hashmap_new_full(nc_hash_create_func create, nc_hash_compare_func compare,
nc_hash_free_func key_free, nc_hash_free_func value_free)
{
return nc_hashmap_new_internal(create, compare, key_free, value_free);
}
static inline unsigned nc_hashmap_get_hash(NcHashmap *self, const void *key)
{
unsigned hash = self->hash(key);
return hash;
}
static int nc_hashmap_insert_bucket(NcHashmap *self, NcHashmapEntry *buckets, int n_buckets,
unsigned hash, const void *key, void *value)
{
NcHashmapEntry *row = &(buckets[hash % (unsigned)n_buckets]);
NcHashmapEntry *head = NULL;
NcHashmapEntry *parent = head = row;
NcHashmapEntry *tomb = NULL;
int ret = 1;
while (row) {
if (!row->occ) {
tomb = row;
}
parent = row;
if (row->occ && self->compare(row->hash, key)) {
if (self->value_free) {
self->value_free(row->value);
}
if (self->key_free) {
self->key_free(row->hash);
}
row->hash = (void *)key;
row->value = value;
return 0;
}
row = row->next;
}
if (tomb) {
row = tomb;
}
if (!row) {
row = calloc(1, sizeof(NcHashmapEntry));
if (!row) {
return -1;
}
}
row->hash = (void *)key;
row->value = value;
row->occ = true;
if (parent != row && parent) {
parent->next = row;
}
return ret;
}
bool nc_hashmap_put(NcHashmap *self, const void *key, void *value)
{
if (!self) {
return false;
}
int inc;
if (nc_hashmap_maybe_resize(self)) {
if (!nc_hashmap_resize(self)) {
return false;
}
}
unsigned hash = nc_hashmap_get_hash(self, key);
inc = nc_hashmap_insert_bucket(self, self->buckets, self->n_buckets, hash, key, value);
if (inc >= 0) {
self->size += inc;
return true;
} else {
return false;
}
}
static NcHashmapEntry *nc_hashmap_get_entry(NcHashmap *self, const void *key)
{
if (!self) {
return NULL;
}
unsigned hash = nc_hashmap_get_hash(self, key);
NcHashmapEntry *row = &(self->buckets[hash % (unsigned)self->n_buckets]);
while (row) {
if (self->compare(row->hash, key)) {
return row;
}
row = row->next;
}
return NULL;
}
void *nc_hashmap_get(NcHashmap *self, const void *key)
{
if (!self) {
return NULL;
}
NcHashmapEntry *row = nc_hashmap_get_entry(self, key);
if (row) {
return row->value;
}
return NULL;
}
static bool nc_hashmap_remove_internal(NcHashmap *self, const void *key, bool remove)
{
if (!self) {
return false;
}
NcHashmapEntry *row = nc_hashmap_get_entry(self, key);
if (!row) {
return false;
}
if (remove) {
if (self->key_free) {
self->key_free(row->hash);
}
if (self->value_free) {
self->value_free(row->value);
}
}
self->size -= 1;
row->hash = NULL;
row->value = NULL;
row->occ = false;
return true;
}
bool nc_hashmap_steal(NcHashmap *self, const void *key)
{
return nc_hashmap_remove_internal(self, key, false);
}
bool nc_hashmap_remove(NcHashmap *self, const void *key)
{
return nc_hashmap_remove_internal(self, key, true);
}
bool nc_hashmap_contains(NcHashmap *self, const void *key)
{
return (nc_hashmap_get(self, key)) != NULL;
}
static inline void nc_hashmap_free_bucket(NcHashmap *self, NcHashmapEntry *bucket, bool nuke)
{
if (!self) {
return;
}
NcHashmapEntry *tmp = bucket;
NcHashmapEntry *bk = bucket;
NcHashmapEntry *root = bucket;
while (tmp) {
bk = NULL;
if (tmp->next) {
bk = tmp->next;
}
if (nuke && tmp->occ) {
if (self->key_free) {
self->key_free(tmp->hash);
}
if (self->value_free) {
self->value_free(tmp->value);
}
}
if (tmp != root) {
free(tmp);
}
tmp = bk;
}
}
void nc_hashmap_free(NcHashmap *self)
{
if (!self) {
return;
}
for (int i = 0; i < self->n_buckets; i++) {
NcHashmapEntry *row = &(self->buckets[i]);
nc_hashmap_free_bucket(self, row, true);
}
if (self->buckets) {
free(self->buckets);
}
free(self);
}
static void nc_hashmap_update_next_size(NcHashmap *self)
{
if (!self) {
return;
}
self->next_size = (int)(self->n_buckets * FULL_FACTOR);
}
int nc_hashmap_size(NcHashmap *self)
{
if (!self) {
return -1;
}
return self->size;
}
static bool nc_hashmap_resize(NcHashmap *self)
{
if (!self || !self->buckets) {
return false;
}
NcHashmapEntry *old_buckets = self->buckets;
NcHashmapEntry *new_buckets = NULL;
NcHashmapEntry *entry = NULL;
int incr;
int old_size, new_size;
int items = 0;
new_size = old_size = self->n_buckets;
new_size *= INCREASE_FACTOR;
new_buckets = calloc((size_t)new_size, sizeof(NcHashmapEntry));
if (!new_buckets) {
return false;
}
for (int i = 0; i < old_size; i++) {
entry = &(old_buckets[i]);
while (entry) {
if (entry->occ) {
unsigned hash = nc_hashmap_get_hash(self, entry->hash);
if ((incr = nc_hashmap_insert_bucket(self,
new_buckets,
new_size,
hash,
entry->hash,
entry->value)) > 0) {
items += incr;
} else {
/* Likely a memory issue */
goto failure;
}
}
entry = entry->next;
}
}
/* Successfully resized - do this separately because we need to
* guarantee old data is preserved */
for (int i = 0; i < old_size; i++) {
nc_hashmap_free_bucket(self, &(old_buckets[i]), false);
}
free(old_buckets);
self->n_buckets = new_size;
self->size = items;
self->buckets = new_buckets;
nc_hashmap_update_next_size(self);
return true;
failure:
for (int i = 0; i < new_size; i++) {
nc_hashmap_free_bucket(self, &(new_buckets[i]), true);
}
free(new_buckets);
return false;
}
void nc_hashmap_iter_init(NcHashmap *map, NcHashmapIter *citer)
{
_NcHashmapIter *iter = NULL;
if (!map || !citer) {
return;
}
iter = (_NcHashmapIter *)citer;
_NcHashmapIter it = {
.bucket = -1, .map = map, .item = NULL,
};
*iter = it;
}
bool nc_hashmap_iter_next(NcHashmapIter *citer, void **key, void **value)
{
_NcHashmapIter *iter = NULL;
NcHashmapEntry *item = NULL;
NcHashmap *map = NULL;
int n_buckets = 0;
if (!citer) {
return false;
}
iter = (_NcHashmapIter *)citer;
map = iter->map;
if (!map) {
return false;
}
n_buckets = map->n_buckets;
item = iter->item;
for (;;) {
if (iter->bucket >= n_buckets) {
if (item && !item->next) {
return false;
}
}
if (!item) {
iter->bucket++;
if (iter->bucket > n_buckets - 1) {
return false;
}
item = &(map->buckets[iter->bucket]);
}
if (item) {
if (item->occ) {
goto success;
}
item = item->next;
}
}
return false;
success:
iter->item = item->next;
if (key) {
*key = item->hash;
}
if (value) {
*value = item->value;
}
return true;
}
/*
* Editor modelines - https://www.wireshark.org/tools/modelines.html
*
* Local variables:
* c-basic-offset: 8
* tab-width: 8
* indent-tabs-mode: nil
* End:
*
* vi: set shiftwidth=8 tabstop=8 expandtab:
* :indentSize=8:tabSize=8:noTabs=true:
*/
+241
View File
@@ -0,0 +1,241 @@
/*
* This file is part of libnica.
*
* Copyright © 2016-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 Hashmap implementation. It focuses on being clean
* and efficient, and is comparable (at -O2) to open addressing hashmaps
* up until around 105,000 elements, where open addressing will begin
* to come out in front. At 1 million elements, open addressing is a clear
* winner, however currently we only need a maximum of 70k elements in
* our implementation.
*
* Given the memory required to even begin to deal with 1 million elements,
* it becomes very questionable whether a hashmap should have even been
* used in the first place (mmap'd db?)
*/
#pragma once
#define _GNU_SOURCE
#include <stdbool.h>
#include <stdint.h>
#include "macros.h"
#include "util.h"
/* Convert between uint and void* */
#define NC_HASH_KEY(x) ((void *)((uintptr_t)(x)))
#define NC_HASH_VALUE(x) NC_HASH_KEY(x)
#define NC_UNHASH_KEY(x) ((unsigned int)((uintptr_t)(x)))
#define NC_UNHASH_VALUE(x) NC_UNHASH_KEY(x)
typedef struct NcHashmap NcHashmap;
/**
* Iteration object
*/
typedef struct NcHashmapIter {
int n0;
void *n1;
void *n2;
} NcHashmapIter;
/**
* Hash comparison function definition
*
* @param l First value to compare
* @param r Second value to compare
*
* @return true if l and r both match, otherwise false
*/
typedef bool (*nc_hash_compare_func)(const void *l, const void *r);
/**
* Hash creation function definition
*
* @param key Key to generate a hash for
*
* @return an unsigned integer hash result
*/
typedef unsigned (*nc_hash_create_func)(const void *key);
/**
* Callback definition to free keys and values
*
* @param p Single-depth pointer to either a key or value that should be freed
*/
typedef void (*nc_hash_free_func)(void *p);
/**
* Default hash/comparison functions
*
* @note These are only used for comparison of *keys*, not values. Unless
* explicitly using string keys, you should most likely stick with the default
* nc_simple_hash and nc_simple_compare functions
*/
/* Default string hash */
static inline unsigned nc_string_hash(const void *key)
{
unsigned hash = 5381;
const signed char *c;
/* DJB's hash function */
for (c = key; *c != '\0'; c++) {
hash = (hash << 5) + hash + (unsigned)*c;
}
return hash;
}
/**
* Trivial pointer->uint hash
*/
static inline unsigned nc_simple_hash(const void *source)
{
return NC_UNHASH_KEY(source);
}
/**
* Comparison of string keys
*/
static inline bool nc_string_compare(const void *l, const void *r)
{
if (!l || !r) {
return false;
}
return (strcmp(l, r) == 0);
}
/**
* Trivial pointer comparison
*/
static inline bool nc_simple_compare(const void *l, const void *r)
{
return (l == r);
}
/**
* Create a new NcHashmap
*
* @param hash Hash creation function
* @param compare Key comparison function
*
* @return A newly allocated NcHashmap
*/
_nica_public_ NcHashmap *nc_hashmap_new(nc_hash_create_func hash, nc_hash_compare_func compare);
/**
* Create a new NcHashmap with cleanup functions
*
* @param hash Hash creation function
* @param compare Key comparison function
* @param key_free Function to free keys when removed/destroyed
* @param value_free Function to free values when removed/destroyed
*
* @return A newly allocated NcHashmap
*/
_nica_public_ NcHashmap *nc_hashmap_new_full(nc_hash_create_func hash, nc_hash_compare_func compare,
nc_hash_free_func key_free,
nc_hash_free_func value_free);
/**
* Store a key/value pair in the hashmap
*
* @note This will displace duplicate keys, and may free both the key
* and value if key_free and value_free are non null
*
* @param key Key to store in the hashmap
* @param value Value to be associated with the key
*
* @return true if the operation succeeded.
*/
_nica_public_ bool nc_hashmap_put(NcHashmap *map, const void *key, void *value);
/**
* Get the value associated with the unique key
*
* @param key Unique key to obtain a value for
* @return The associated value if it exists, otherwise NULL
*/
_nica_public_ void *nc_hashmap_get(NcHashmap *map, const void *key);
/**
* Determine if the key has an associated value in the NcHashmap
*
* @param key Unique key to check value for
* @return True if the key exists in the hashmap
*/
_nica_public_ bool nc_hashmap_contains(NcHashmap *self, const void *key);
/**
* Free the given NcHashmap, and all keys/values if appropriate
*/
_nica_public_ void nc_hashmap_free(NcHashmap *map);
/**
* Remove the value and key identified by key.
*
* @note If key_free or value_free are non-NULL, they will be invoked
* for both the key and value being removed
*
* @param key The unique key to remove
* @return true if the key/value pair were removed
*/
_nica_public_ bool nc_hashmap_remove(NcHashmap *map, const void *key);
/**
* Remove the value and key identified by key without freeing them
*
* @return true if the key/value pair were stolen
*/
_nica_public_ bool nc_hashmap_steal(NcHashmap *map, const void *key);
/**
* Return the current size of the hashmap
*
* @return size of the current hashmap (element count)
*/
_nica_public_ int nc_hashmap_size(NcHashmap *map);
/**
* Initialise a NcHashmapIter for iteration purposes
*
* @note The iter *must* be re-inited to re-use, or if it becomes
* exhausted from a previous iteration run
*
* @param iter pointer to a NcHashmapIter
*/
_nica_public_ void nc_hashmap_iter_init(NcHashmap *map, NcHashmapIter *iter);
/**
* Iterate every key/value pair in the hashmap
*
* @param iter A correctly initialised NcHashmapIter
* @param key Pointers to store the key in, may be NULL to skip
* @param value Pointer to store the value in, may be NULL to skip
*
* @return true if it's possible to iterate
*/
_nica_public_ bool nc_hashmap_iter_next(NcHashmapIter *iter, void **key, void **value);
DEF_AUTOFREE(NcHashmap, nc_hashmap_free)
/*
* Editor modelines - https://www.wireshark.org/tools/modelines.html
*
* Local variables:
* c-basic-offset: 8
* tab-width: 8
* indent-tabs-mode: nil
* End:
*
* vi: set shiftwidth=8 tabstop=8 expandtab:
* :indentSize=8:tabSize=8:noTabs=true:
*/
+398
View File
@@ -0,0 +1,398 @@
/*
* This file is part of libnica.
*
* Copyright © 2016-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 <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "hashmap.h"
#include "inifile.h"
/**
* Mapping of @NcIniError to static strings
*/
static const char *_errors[] =
{[NC_INI_ERROR_FILE] = "",
[NC_INI_ERROR_EMPTY_KEY] = "Encountered empty key",
[NC_INI_ERROR_NOT_CLOSED] = "Expected closing \']\' for section",
[NC_INI_ERROR_NO_SECTION] = "Encountered key=value mapping without a valid section",
[NC_INI_ERROR_INVALID_LINE] = "Expected key=value notation",
[NC_INI_ERROR_INTERNAL] = "A fatal internal error was encountered" };
const char *nc_ini_error(enum NcIniError error)
{
int j = abs(error);
if (j < NC_INI_ERROR_FILE || j >= NC_INI_ERROR_MAX) {
return "[Unknown Error]";
}
return _errors[j];
}
/**
* Strip leading whitespace from string (left)
*/
static char *lstrip(char *str, ssize_t len, ssize_t *out_len)
{
ssize_t skip_len = 0;
for (ssize_t i = 0; i < len; i++) {
if (str[i] == ' ' || str[i] == '\t') {
++skip_len;
continue;
}
break;
}
*out_len = len - skip_len;
if (skip_len > 0) {
char *c = strdup(str + skip_len);
free(str);
return c;
}
return str + skip_len;
}
/**
* Strip trailing whitespace from string (right)
*/
static char *rstrip(char *str, ssize_t len, ssize_t *out_len)
{
ssize_t skip_len = 0;
for (ssize_t i = len; i > 0; i--) {
if (str[i] == ' ' || str[i] == '\t') {
++skip_len;
continue;
} else if (str[i] == '\0') {
continue;
}
break;
}
*out_len = len - skip_len;
if (len == skip_len) {
return str;
}
str[(len - skip_len)] = '\0';
return str;
}
/**
* Chew both ends of a null terminated string of whitespace
* If the left side is whitespace padded, this will always result
* in a new allocation due to fast-forwarding the pointer. Keep
* this in mind.
*/
static char *string_chew_terminated(char *inp)
{
int skip_offset = 0;
int len = 0;
int end_len = 0;
bool count = true;
if (!inp) {
return NULL;
}
for (char *c = inp; *c; c++) {
if (count && (*c == ' ' || *c == '\t')) {
++skip_offset;
continue;
} else {
count = false;
}
++len;
}
for (int i = len; i > skip_offset; i--) {
if (inp[i] == ' ' || inp[i] == '\t') {
++end_len;
continue;
} else if (inp[i] == '\0') {
continue;
}
break;
}
if (end_len > 0) {
inp[(len - end_len)] = '\0';
}
if (skip_offset > 0) {
char *c = strdup(inp + skip_offset);
free(inp);
return c;
}
return inp;
}
/**
* Munch both ends of the string
*/
static char *string_strip(char *str, ssize_t len, ssize_t *out_len)
{
ssize_t mylen = len;
char *c = lstrip(str, len, &mylen);
c = rstrip(c, mylen, &mylen);
if (out_len) {
*out_len = mylen;
}
return c;
}
/* eat \s */
static char *string_unescape(char *str)
{
char *c, *c1, *c2, *c3;;
if (!str) {
return NULL;
}
c = calloc(strlen(str) + 1, 1);
if (c == NULL) {
return NULL;
}
c1 = c;
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;
}
NcHashmap *nc_ini_file_parse(const char *path)
{
NcHashmap *ret = NULL;
int error_line = 0;
int r = 0;
r = nc_ini_file_parse_full(path, &ret, &error_line);
if (r != 0) {
if (abs(r) == NC_INI_ERROR_FILE) {
fprintf(stderr, "[inifile] %s: %s\n", strerror(errno), path);
} else {
fprintf(stderr,
"[inifile] %s [L%d]: %s\n",
nc_ini_error(r),
error_line,
path);
}
return NULL;
}
return ret;
}
int nc_ini_file_parse_full(const char *path, NcHashmap **out_map, int *error_line_number)
{
autofree(FILE) *file = NULL;
char *buf = NULL;
ssize_t r = 0;
size_t sn = 0;
int line_count = 1;
char *current_section = NULL;
NcHashmap *root_map = NULL;
NcHashmap *section_map = NULL;
bool failed = false;
int err_ret = 0;
file = fopen(path, "r");
if (!file) {
return -(NC_INI_ERROR_FILE);
}
if (!out_map) {
fprintf(stderr, "nc_ini_file_parse_full(): NcHashmap pointer invalid\n");
return -1;
}
root_map = nc_hashmap_new_full(nc_string_hash,
nc_string_compare,
free,
(nc_hash_free_func)nc_hashmap_free);
while ((r = getline(&buf, &sn, file)) != -1) {
char *ch = NULL;
ssize_t str_len = r;
char *key = NULL;
char *value = NULL;
/* Fix newline */
if (buf[r - 1] == '\n') {
buf[r - 1] = '\0';
--r;
}
str_len = r;
buf = string_strip(buf, r, &str_len);
/* Empty lines are fine */
if (streq(buf, "")) {
goto next;
}
if (buf[0] == '[') {
/* Validate section start */
if (buf[str_len - 1] != ']') {
/* Throw error */
err_ret = NC_INI_ERROR_NOT_CLOSED;
goto fail;
}
/* Grab the section name, and "close" last section */
buf[str_len - 1] = '\0';
if (current_section) {
free(current_section);
}
current_section = strdup(buf + 1);
section_map = nc_hashmap_get(root_map, current_section);
if (!section_map) {
/* Create a new section dynamically */
section_map = nc_hashmap_new_full(nc_string_hash,
nc_string_compare,
free,
free);
nc_hashmap_put(root_map, strdup(current_section), section_map);
}
goto next;
} else if (buf[0] == '#' || buf[0] == ';') {
/* Skip comment */
goto next;
}
/* Look for key = value */
ch = strchr(buf, '=');
if (!ch) {
/* Throw error */
err_ret = NC_INI_ERROR_INVALID_LINE;
goto fail;
}
/* Can't have sectionless k->v */
if (!current_section) {
err_ret = NC_INI_ERROR_NO_SECTION;
goto fail;
}
long int offset = ch - buf;
/* Grab the key->value from this assignment line */
value = strdup((buf + offset) + 1);
buf[offset] = '\0';
key = strdup(buf);
key = string_chew_terminated(key);
value = string_chew_terminated(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;
free(key);
key = NULL;
free(value);
value = NULL;
goto fail;
}
/* Ensure a section mapping exists */
section_map = nc_hashmap_get(root_map, current_section);
if (!section_map) {
err_ret = NC_INI_ERROR_INTERNAL;
fprintf(stderr,
"[inifile] Fatal! No section map for named "
"section: %s\n",
current_section);
goto fail;
}
/* Insert these guys into the map */
if (!nc_hashmap_put(section_map, key, value)) {
err_ret = NC_INI_ERROR_INTERNAL;
fprintf(stderr, "[inifile] Fatal! Out of memory\n");
goto fail;
}
/* Progression + cleanup */
next:
if (buf) {
free(buf);
buf = NULL;
}
++line_count;
sn = 0;
continue;
/* Parsing error, bail */
fail:
failed = true;
if (error_line_number) {
*error_line_number = line_count;
}
if (key) {
free(key);
}
if (value) {
free(value);
}
break;
}
if (buf) {
free(buf);
buf = NULL;
}
if (current_section) {
free(current_section);
current_section = NULL;
}
if (failed) {
if (root_map) {
nc_hashmap_free(root_map);
}
return -(err_ret);
}
*out_map = root_map;
return 0;
}
/*
* Editor modelines - https://www.wireshark.org/tools/modelines.html
*
* Local variables:
* c-basic-offset: 8
* tab-width: 8
* indent-tabs-mode: nil
* End:
*
* vi: set shiftwidth=8 tabstop=8 expandtab:
* :indentSize=8:tabSize=8:noTabs=true:
*/
+114
View File
@@ -0,0 +1,114 @@
/*
* This file is part of libnica.
*
* Copyright © 2016-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.
*/
#pragma once
#include "hashmap.h"
#include "macros.h"
#include "util.h"
/**
* The Nica INI Parser focuses on simplicity and ease of use, therefore it
* currently only supports a read-only parse operation.
*
* The parse functions are used to return a _root_ @NcHashmap which contains
* name to section mappings. Each section is in turn an @NcHashmap with string
* to string mappings.
*
* Consider the following example:
*
* [Person]
* alive = true
* ; This person is alive
*
* The returned @NcHashmap will contain the key "Person", which corresponds to
* another @NcHashmap. This @NcHashmap will contain the key "alive", which has
* the value "true". Therefore we can access this variable using the @NcHashmap
* API:
*
* char *alive = nc_hashmap_get(nc_hashmap_get(ini, "Person"), "alive");
*
* Conversely, we can use the API to determine whether a section or key is set:
*
* if (nc_hashmap_contains(ini, "Person"))
*
* As @NcHashmap returns NULL in nc_hashmap_get, you can happily pass NULL
* keys, allowing quick key fetches without checking if the section exists.
*
* In our INI file, lines beginning with ";" or "#" are considered as comments
* and are not processed. A line following the "key = value" notation is an
* _assignment_. "[Section]" is considered to be a section in the INI file, and
* all INI files must have at least one section.
*
* We do allow duplication section definitions, by following a merge policy. If
* a key is redefined then the original key->value mapping is freed and the new
* key->value mapping is inserted.
*
* The parser will return a correctly configured @NcHashmap, the only cleanup
* required is to call nc_hashmap_free on it. Alternatively, use the autofree
* helper, for a RAII approach:
*
* autofree(NcHashmap) *map = nc_ini_file_parse("myfile.ini");
*/
/**
* Errors that are reported when parsing an ini file
*/
enum NcIniError {
NC_INI_ERROR_MIN = 0,
NC_INI_ERROR_FILE, /**< File based error, check strerror(errno) */
NC_INI_ERROR_EMPTY_KEY, /**< Empty key in an assignment line */
NC_INI_ERROR_NOT_CLOSED, /**< Encountered section start that wasn't closed with a ']' */
NC_INI_ERROR_NO_SECTION, /**< Key assignment with no defined sections */
NC_INI_ERROR_INVALID_LINE, /**< Encountered an invalid line (syntax) */
NC_INI_ERROR_INTERNAL, /**< Fatal internal error encountered. Caller should exit */
NC_INI_ERROR_MAX
};
/**
* Convenience wrapper for nc_ini_file_parse_full, reports errors to stderrr
* and returns an NcHashmap if the parsing succeeded
*/
_nica_public_ NcHashmap *nc_ini_file_parse(const char *path);
/**
* Parse an INI file into a hash of hashes.
*
* @param path Path on the filesystem to parse, must be in INI syntax.
* @param out_map Pointer to store the resulting root NcHashmap in
* @param error_line_number If not null, then the erronous line number is
* stored.
*
* @return 0 if the call was successful, or a negative integer. See nc_ini_error
*/
_nica_public_ int nc_ini_file_parse_full(const char *path, NcHashmap **out_map,
int *error_line_number);
/**
* Return a string representation for a given @NcIniError
*
* @param error Valid NcIniError code
* @return a static string owned by the implementation
*/
_nica_public_ const char *nc_ini_error(enum NcIniError error);
/*
* Editor modelines - https://www.wireshark.org/tools/modelines.html
*
* Local variables:
* c-basic-offset: 8
* tab-width: 8
* indent-tabs-mode: nil
* End:
*
* vi: set shiftwidth=8 tabstop=8 expandtab:
* :indentSize=8:tabSize=8:noTabs=true:
*/
+6
View File
@@ -0,0 +1,6 @@
noinst_HEADERS += \
%D%/hashmap.h \
%D%/inifile.h \
%D%/macros.h \
%D%/nc-string.h \
%D%/util.h
+29
View File
@@ -0,0 +1,29 @@
/*
* This file is part of libnica.
*
* Copyright © 2016-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.
*/
#pragma once
#define _GNU_SOURCE
#define _nica_public_ __attribute__((visibility("default")))
/*
* Editor modelines - https://www.wireshark.org/tools/modelines.html
*
* Local variables:
* c-basic-offset: 8
* tab-width: 8
* indent-tabs-mode: nil
* End:
*
* vi: set shiftwidth=8 tabstop=8 expandtab:
* :indentSize=8:tabSize=8:noTabs=true:
*/
+144
View File
@@ -0,0 +1,144 @@
/*
* This file is part of libnica.
*
* Copyright © 2016-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 <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "nc-string.h"
nc_string *nc_string_dup(const char *str)
{
if (!str) {
return NULL;
}
struct nc_string_t *st = calloc(1, sizeof(struct nc_string_t));
if (!st) {
return NULL;
}
st->len = asprintf(&st->str, "%s", str);
if (st->len < 0 || !st->str) {
free(st);
return NULL;
}
return st;
}
nc_string *nc_string_dup_printf(const char *ptn, ...)
{
if (!ptn) {
return NULL;
}
struct nc_string_t *st = calloc(1, sizeof(struct nc_string_t));
if (!st) {
return NULL;
}
va_list va;
va_start(va, ptn);
st->len = vasprintf(&st->str, ptn, va);
if (st->len < 0 || !st->str) {
free(st);
st = NULL;
goto end;
}
end:
va_end(va);
return st;
}
nc_string *nc_string_append_printf(nc_string *st, const char *ptn, ...)
{
char *newstr;
char *newstr2;
int ret;
if (!ptn) {
return NULL;
}
va_list va;
va_start(va, ptn);
ret = vasprintf(&newstr, ptn, va);
if (ret <= 0)
newstr = strdup("");
st->len = asprintf(&newstr2, "%s%s", st->str, newstr);
free(st->str);
st->str = newstr2;
free(newstr);
va_end(va);
return st;
}
bool nc_string_cat(nc_string *s, const char *append)
{
char *p = NULL;
int len = 0;
if (!s || !append) {
return false;
}
if (!s->str) {
return false;
}
len = asprintf(&p, "%s%s", s->str, append);
if (!p || len < s->len) {
return false;
}
free(s->str);
s->str = p;
s->len = len;
return true;
}
bool nc_string_prepend(nc_string *s, const char *prepend)
{
char *p = NULL;
int len = 0;
if (!s || !prepend) {
return false;
}
if (!s->str) {
return false;
}
len = asprintf(&p, "%s%s", prepend, s->str);
if (!p || len < s->len) {
return false;
}
free(s->str);
s->str = p;
s->len = len;
return true;
}
/*
* Editor modelines - https://www.wireshark.org/tools/modelines.html
*
* Local variables:
* c-basic-offset: 8
* tab-width: 8
* indent-tabs-mode: nil
* End:
*
* vi: set shiftwidth=8 tabstop=8 expandtab:
* :indentSize=8:tabSize=8:noTabs=true:
*/
+220
View File
@@ -0,0 +1,220 @@
/*
* This file is part of libnica.
*
* Copyright © 2016-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.
*/
#pragma once
#define _GNU_SOURCE
#include <assert.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include "macros.h"
/**
* Safely represent and store a buffer as a string
*/
typedef struct nc_string_t {
char *str; /**<Buffer holding a NUL-terminated string */
int len; /**<Current length of the string */
} nc_string;
/**
* Construct a new string using printf style syntax
*
* @param format Printf-style format string
* @param ... Variable arguments
*
* @return a new nc_string if allocation succeeded, otherwise NULL
*/
_nica_public_ nc_string *nc_string_dup_printf(const char *format, ...)
__attribute__((format(printf, 1, 2)));
_nica_public_ nc_string *nc_string_append_printf(nc_string *st, const char *ptn, ...)
__attribute__((format(printf, 2, 3)));
/**
* Duplicate a string into a new NUL-terminated nc_string
*
* @param str Pointer to a string
* @return a new nc_string if allocation succeeded, otherwise NULL
*/
_nica_public_ nc_string *nc_string_dup(const char *str);
/**
* Free an existing string and its contents
*
* @param str Pointer to an existing string
*/
static inline void nc_string_free(nc_string *str)
{
if (!str) {
return;
}
if (str->str) {
free(str->str);
}
free(str);
}
/**
* Append the contents of 'append' into the given nc_string
*
* @param str Pointer to a valid nc_string
* @param append Text to append into the nc_string
*
* @return a boolean value indicating success
*/
_nica_public_ bool nc_string_cat(nc_string *str, const char *append);
_nica_public_ bool nc_string_prepend(nc_string *s, const char *prepend);
/**
* Determine if string A is equal to string B
*
* @note This function will not check beyond the length of string A
*
* @param a string to check
* @param b string to check against
* @return a boolean value, true if the strings match, otherwise false
*/
static inline bool nc_string_equal(nc_string *a, nc_string *b)
{
if (!a || !b) {
return false;
}
if (!a->str || !b->str) {
return false;
}
return strncmp(a->str, b->str, (size_t)a->len) == 0;
}
/**
* Determine if string A is equal to string B
*
* @note This function will not check beyond the length of string A
*
* @param a string to check
* @param b const char* string to check against
* @return a boolean value, true if the strings match, otherwise false
*/
static inline bool nc_string_const_equal(nc_string *a, const char *b)
{
if (!a || !b) {
return false;
}
if (!a->str) {
return false;
}
return strncmp(a->str, b, (size_t)a->len) == 0;
}
/**
* Convenience wrapper to obtain string length
*
* @note This will assert the string is not NULL, ensuring a termination.
*
* @return length of the given string
*/
static inline int ncstrlen(nc_string *str)
{
assert(str != NULL);
return str->len;
}
/**
* Determine if a string has the given suffix
*
* @param str Valid nc_string
* @param suffix A suffix to check
* @param len Length of the suffix
*
* @return True if @str has the given suffix
*/
static inline bool nc_string_has_suffix_const(nc_string *str, char *suffix, ssize_t len)
{
if (!str || !suffix) {
return false;
}
if (len > str->len || len <= 0) {
return false;
}
return (strncmp(str->str + (str->len - (len)), suffix, (size_t)len) == 0);
}
/**
* Determine if a string has the given prefix
*
* @param str Valid nc_string
* @param prefix A prefix to check
* @param len Length of the prefix
*
* @return True if @str has the given prefix
*/
static inline bool nc_string_has_prefix_const(nc_string *str, char *prefix, ssize_t len)
{
if (!str || !prefix) {
return false;
}
if (len > str->len || len <= 0) {
return false;
}
return (strncmp(str->str, prefix, (size_t)len) == 0);
}
/**
* Perform inplace modification of a strip to return
* a stripped pointer, that is, without start and end whitespace.
*
* This does not modify the length of the string.
*/
static inline char *nc_string_strip(nc_string *str)
{
int i;
if (!str || !str->str) {
return NULL;
}
for (i = 0; i < str->len; i++) {
if (str->str[i] != ' ') {
break;
}
}
for (int j = str->len - 1; j > i; j--) {
if (str->str[j] != ' ') {
str->str[j + 1] = '\0';
break;
}
}
return str->str + i;
}
/**
* To be used only with compile time constants.
*/
#define nc_string_has_suffix(a, suff) nc_string_has_suffix_const(a, suff, (sizeof(suff) - 1))
#define nc_string_has_prefix(a, pref) nc_string_has_prefix_const(a, pref, (sizeof(pref) - 1))
/*
* Editor modelines - https://www.wireshark.org/tools/modelines.html
*
* Local variables:
* c-basic-offset: 8
* tab-width: 8
* indent-tabs-mode: nil
* End:
*
* vi: set shiftwidth=8 tabstop=8 expandtab:
* :indentSize=8:tabSize=8:noTabs=true:
*/
+69
View File
@@ -0,0 +1,69 @@
/*
* This file is part of libnica.
*
* Copyright © 2016-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.
*/
#pragma once
#define _GNU_SOURCE
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <nica/macros.h>
#define DEF_AUTOFREE(N, C) \
static inline void _autofree_func_##N(void *p) \
{ \
if (p && *(N **)p) { \
C(*(N **)p); \
(*(void **)p) = NULL; \
} \
}
#define autofree(N) __attribute__((cleanup(_autofree_func_##N))) N
/**
* Dump any leaked file descriptors
*/
_nica_public_ void nc_dump_file_descriptor_leaks(void);
_nica_public_ void *greedy_realloc(void **p, size_t *allocated, size_t need);
/**
* Test string equality of two null terminated strings
*/
__attribute__((always_inline)) static inline bool streq(const char *a, const char *b)
{
if (a == b) {
return true;
}
if (!a || !b) {
return false;
}
return strcmp(a, b) == 0;
}
DEF_AUTOFREE(char, free)
DEF_AUTOFREE(FILE, fclose)
/*
* Editor modelines - https://www.wireshark.org/tools/modelines.html
*
* Local variables:
* c-basic-offset: 8
* tab-width: 8
* indent-tabs-mode: nil
* End:
*
* vi: set shiftwidth=8 tabstop=8 expandtab:
* :indentSize=8:tabSize=8:noTabs=true:
*/
+97
View File
@@ -0,0 +1,97 @@
/*
* This program is part of the Clear Linux Project
*
* Copyright 2018 Intel Corporation
*
* This program is free software; you can redistribute it and/or modify it under
* the terms and conditions of the GNU Lesser General Public License, as
* published by the Free Software Foundation; either version 2.1 of the License,
* or (at your option) any later version.
*
* This program is distributed in the hope it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*/
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <unistd.h>
#include <stdbool.h>
#include <sys/stat.h>
#include "telemetry.h"
#include "log.h"
#include "spool.h"
#include "configuration.h"
#include "telempostdaemon.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.
*/
bool (*post_record_ptr)(char *[], char *, char *) = post_record_http;
void print_usage(char *prog)
{
printf("%s: Usage\n", prog);
printf(" -f, --config_file Configuration file. This overides the other parameters\n");
printf(" -h, --help Display this help message\n");
printf(" -V, --version Print the program version\n");
}
int main(int argc, char **argv)
{
int c;
int opt_index = 0;
TelemPostDaemon daemon;
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 'V':
printf(PACKAGE_VERSION "\n");
exit(EXIT_SUCCESS);
case 'h':
print_usage(argv[0]);
exit(EXIT_SUCCESS);
case '?':
print_usage(argv[0]);
exit(EXIT_FAILURE);
}
}
initialize_post_daemon(&daemon);
daemon.current_spool_size = get_spool_dir_size();
/* When path activated this will process
* the activating message or previously
* spooled data */
staging_records_loop(&daemon);
/* This function is blocking, it will
* block until a signal is received */
run_daemon(&daemon);
close_daemon(&daemon);
return 0;
}
+16 -50
View File
@@ -31,6 +31,7 @@
#include <signal.h>
#include <malloc.h>
#include "telemetry.h"
#include "config.h"
#include "common.h"
#ifdef HAVE_SYSTEMD_SD_DAEMON_H
@@ -49,14 +50,6 @@
#include "log.h"
#include "telemdaemon.h"
#include "configuration.h"
#include "spool.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.
*/
bool (*post_record_ptr)(char *[], char *, bool) = post_record_http;
void print_usage(char *prog)
{
@@ -76,7 +69,6 @@ int main(int argc, char **argv)
client *cl = NULL;
client *current_client = NULL;
int c;
char *config_file = NULL;
int opt_index = 0;
sigset_t mask;
//bool interrupted = false;
@@ -94,18 +86,12 @@ int main(int argc, char **argv)
while ((c = getopt_long(argc, argv, "f:hV", opts, &opt_index)) != -1) {
switch (c) {
case 'f':
config_file = optarg;
struct stat buf;
ret = stat(config_file, &buf);
/* check if the file exists and is a regular file */
if (ret == -1 || !S_ISREG(buf.st_mode)) {
telem_log(LOG_ERR, "Configuration file"
case 'f':
if (tm_set_config_file(optarg) != 0) {
telem_log(LOG_ERR, "Configuration file"
" path not valid\n");
exit(EXIT_FAILURE);
exit(EXIT_FAILURE);
}
set_config_file(config_file);
break;
case 'h':
print_usage(argv[0]);
@@ -119,7 +105,7 @@ int main(int argc, char **argv)
exit(EXIT_FAILURE);
}
}
initialize_daemon(&daemon);
initialize_probe_daemon(&daemon);
sigemptyset(&mask);
@@ -220,19 +206,9 @@ int main(int argc, char **argv)
telem_log(LOG_INFO, "Listening on socket...\n");
/* Calculate the spool directory size */
if (is_spool_valid()) {
daemon.is_spool_valid = true;
daemon.current_spool_size = get_spool_dir_size();
}
bool daemon_recycling_enabled = daemon_recycling_enabled_config();
int spool_process_time = spool_process_time_config();
spool_records_loop(&(daemon.current_spool_size));
time_t last_spool_run_time = time(NULL);
time_t last_daemon_start_time = time(NULL);
bool daemon_recycling_enabled = daemon_recycling_enabled_config();
time_t last_record_received = time(NULL);
ret = update_machine_id();
if (ret == -1) {
@@ -267,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;
}
@@ -276,12 +252,6 @@ int main(int argc, char **argv)
telem_log(LOG_INFO, "Received a SIGHUP signal\n");
/* reload configuration file */
reload_config();
if (is_spool_valid()) {
daemon.is_spool_valid = true;
//calc_spool_dir_size();
daemon.current_spool_size = get_spool_dir_size();
}
}
}
@@ -333,25 +303,20 @@ int main(int argc, char **argv)
}
assert(current_client);
handle_client(&daemon, i, current_client);
last_record_received = time(NULL);
}
}
} else {
time_t now = time(NULL);
/* If spool is empty and time to recycle the daemon has elapsed*/
if (daemon_recycling_enabled && (daemon.current_spool_size == 0) &&
difftime(now, last_daemon_start_time) >= TM_DAEMON_EXIT_TIME) {
/* time to recycle the daemon has elapsed*/
if (daemon_recycling_enabled &&
difftime(now, last_record_received) >= TM_DAEMON_EXIT_TIME) {
/* Exit */
telem_log(LOG_INFO, "Daemon exiting for recycling\n");
goto clean_exit;
}
}
time_t now = time(NULL);
if (difftime(now, last_spool_run_time) >= spool_process_time) {
spool_records_loop(&(daemon.current_spool_size));
last_spool_run_time = time(NULL);
}
if (difftime(now, last_refresh_time) >= TM_REFRESH_RATE) {
int ret = update_machine_id();
if (ret == -1) {
@@ -362,12 +327,13 @@ int main(int argc, char **argv)
}
clean_exit:
/* Free memory before exiting */
while ((cl = LIST_FIRST(&(daemon.client_head))) != NULL) {
remove_client(&(daemon.client_head), cl);
}
free(daemon.pollfds);
free(daemon.machine_id_override);
if (LIST_EMPTY(&(daemon.client_head))) {
telem_log(LOG_INFO, "Client list cleared\n");
}
+50
View File
@@ -0,0 +1,50 @@
# Probes
The default probes provided along the telemetry client code are:
* crashprobe: This probe processes core dump files. It can be registered as
the kernel core file handler in /proc/sys/kernel/core_pattern.
* hprobe: a simple probe that sends a keep alive message. This probe can also
be useful for testing
* journalprobe: a probe that monitors the systemd journal for log messages
from failed services.
* klogscanner: a probe to collect 'oops messages' when the kernel detects a
problem. Also reports errors in the Boot Error Region Table if detected.
* pstoreprobe: probe to collect messages left on pstore filesystem.
* 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.
For example to generate an hprobe like message we can execute the following
command:
```
$ telem-record-gen --class org.example/hello/world --payload hello
```
for more options use the ```--help``` switch i.e.:
```
$ telem-record-gen --help
Usage:
telem-record-gen [OPTIONS] - create and send a custom telemetry record
Help Options:
-h, --help Show help options
Application Options:
-f, --config-file Path to configuration file (not implemented yet)
-V, --version Print the program version
-s, --severity Severity level (1-4) - (default 1)
-c, --class Classification level_1/level_2/level_3
-p, --payload Record body (max size = 8k)
-P, --payload-file File to read payload from
-R, --record-version Version number for format of payload (default 1)
-e, --event-id Event id to use in the record
```
+241 -146
View File
@@ -17,6 +17,7 @@
#define _GNU_SOURCE
#include <assert.h>
#include <fcntl.h>
#include <getopt.h>
#include <grp.h>
#include <inttypes.h>
#include <limits.h>
@@ -31,15 +32,17 @@
#include <sys/stat.h>
#include <sys/types.h>
#include <glib.h>
#include <libelf.h>
#include <elfutils/libdwfl.h>
#include "nica/nc-string.h"
#include "config.h"
#include "log.h"
#include "probe.h"
#include "telemetry.h"
#define FRAMES_MAX 64
static Dwfl *d_core = NULL;
/*
@@ -53,10 +56,10 @@ static Dwfl *d_core = NULL;
static char *debuginfo_path = "-/usr/lib/debug";
static unsigned int frame_counter = 0;
static gchar *proc_name = NULL;
static char *proc_name = NULL;
static pid_t core_for_pid = 0;
static GString *header = NULL;
static gchar *errorstr = NULL;
static nc_string *header = NULL;
static char *errorstr = NULL;
static uint32_t unknown_severity = 2;
static uint32_t default_severity = 3;
@@ -67,6 +70,8 @@ static char clr_build_class[40] = "org.clearlinux/crash/clr-build";
static char error_class[30] = "org.clearlinux/crash/error";
static char unknown_class[30] = "org.clearlinux/crash/unknown";
static char temp_core[] = "/tmp/corefile-XXXXXX";
static const Dwfl_Callbacks cb =
{
.find_elf = dwfl_build_id_find_elf,
@@ -84,9 +89,17 @@ static inline void tm_dwfl_err(const char *msg)
telem_log(LOG_ERR, "%s: %s\n", msg, dwfl_errmsg(-1));
}
static gchar *replace_exclamations(gchar *str)
static char *replace_exclamations(char *str)
{
return g_strdelimit(str, "!", '/');
char *c;
while (1) {
c = strchr(str, '!');
if (!c) {
break;
}
*c = '/';
}
return str;
}
static void drop_privs(void)
@@ -138,23 +151,14 @@ static int temp_core_file(void)
int tmp;
ssize_t ret;
char core[PATH_MAX] = "/tmp/corefile-XXXXXX";
/* mkstemp() opens the file with O_EXCL and 0600 permissions, so no need
* to change umask or manipulate the fd to meet those requirements.
*/
if ((tmp = mkstemp(core)) < 0) {
if ((tmp = mkstemp(temp_core)) < 0) {
telem_perror("Failed to create temp core file");
return -1;
}
#ifndef DEBUG
if (unlink(core) < 0) {
telem_perror("Failed to unlink temp core file");
return -1;
}
#endif
while (true) {
// Use Linux-specific splice(2) here;
// simplifies copying data from pipe->file
@@ -234,15 +238,20 @@ static int frame_cb(Dwfl_Frame *frame, void *userdata)
Dwarf_Addr pc;
Dwarf_Addr pc_adjusted;
Dwfl_Module *module;
Dwfl_Line *line;
const char *procname;
const char *modname;
bool activation;
GString **bt = (GString **)userdata;
nc_string **bt = (nc_string **)userdata;
if (!dwfl_frame_pc(frame, &pc, &activation)) {
errorstr = g_strdup_printf("Failed to find program counter for"
" current frame: %s\n",
dwfl_errmsg(-1));
int ret;
ret = asprintf(&errorstr, "Failed to find program counter for"
" current frame: %s\n",
dwfl_errmsg(-1));
if (ret < 0) {
errorstr = NULL;
}
return DWARF_CB_ABORT;
}
@@ -259,8 +268,8 @@ static int frame_cb(Dwfl_Frame *frame, void *userdata)
module = dwfl_addrmodule(d_core, pc_adjusted);
if (!module) {
errorstr = g_strdup("Failed to find module for current"
" frame\n");
errorstr = strdup("Failed to find module for current"
" frame\n");
return DWARF_CB_ABORT;
}
@@ -268,16 +277,33 @@ static int frame_cb(Dwfl_Frame *frame, void *userdata)
NULL);
procname = dwfl_module_addrname(module, pc_adjusted);
line = dwfl_module_getsrc(module, pc_adjusted);
if (procname && modname) {
g_string_append_printf(*bt, "#%u %s() - [%s]\n",
frame_counter++, procname, modname);
nc_string_append_printf(*bt, "#%u %s() - [%s]",
frame_counter++, procname, modname);
} else if (modname) {
g_string_append_printf(*bt, "#%u ??? - [%s]\n",
frame_counter++, modname);
nc_string_append_printf(*bt, "#%u ??? - [%s]",
frame_counter++, modname);
} else {
// TODO: decide on "no symbol" representation
g_string_append_printf(*bt, "#%u (no symbols)\n",
frame_counter++);
nc_string_append_printf(*bt, "#%u (no symbols)",
frame_counter++);
}
if (line) {
const char *src;
int lineno, linecol;
src = dwfl_lineinfo(line, &pc_adjusted, &lineno, &linecol, NULL, NULL);
if (src) {
nc_string_append_printf(*bt, " - %s:%i", src, lineno);
}
}
nc_string_append_printf(*bt, "\n");
if (frame_counter >= FRAMES_MAX) {
errorstr = NULL;
return DWARF_CB_ABORT;
}
return DWARF_CB_OK;
@@ -286,23 +312,25 @@ static int frame_cb(Dwfl_Frame *frame, void *userdata)
static int thread_cb(Dwfl_Thread *thread, void *userdata)
{
int ret;
GString **bt = (GString **)userdata;
nc_string **bt = (nc_string **)userdata;
pid_t tid;
tid = dwfl_thread_tid(thread);
g_string_append_printf(*bt, "\nBacktrace (TID %u):\n",
(unsigned int)tid);
nc_string_append_printf(*bt, "\nBacktrace (TID %u):\n",
(unsigned int)tid);
ret = dwfl_thread_getframes(thread, frame_cb, userdata);
switch (ret) {
case -1:
errorstr = g_strdup_printf("Error while iterating"
" through frames for thread"
" %u: %s\n",
(unsigned int)tid,
dwfl_errmsg(-1));
if (asprintf(&errorstr, "Error while iterating"
" through frames for thread"
" %u: %s\n",
(unsigned int)tid,
dwfl_errmsg(-1)) < 0) {
errorstr = NULL;
}
return DWARF_CB_ABORT;
case DWARF_CB_ABORT:
/* already set the error string in frame_cb */
@@ -318,14 +346,14 @@ static int thread_cb(Dwfl_Thread *thread, void *userdata)
frame_counter = 0;
#if 0
g_string_append_printf(*bt, "\nRegisters (TID %u):\nTODO\n", current,
(unsigned int)tid);
nc_string_append_printf(*bt, "\nRegisters (TID %u):\nTODO\n", current,
(unsigned int)tid);
#endif
return DWARF_CB_OK;
}
static bool send_data(GString **backtrace, uint32_t severity, char *class)
static bool send_data(nc_string **backtrace, uint32_t severity, char *class)
{
struct telem_ref *handle = NULL;
int ret;
@@ -359,17 +387,24 @@ fail:
* which must be initialized prior to calling this function. The callback
* function for processing each core thread is passed as the second argument to
* dwfl_getthreads(). The backtrace argument is the address of a pointer to a
* GString object declared by the caller which stores the backtrace data as a
* nc_string object declared by the caller which stores the backtrace data as a
* string.
*/
static int process_corefile(GString **backtrace)
static int process_corefile(nc_string **backtrace)
{
if (*backtrace) {
g_string_free(*backtrace, TRUE);
nc_string_free(*backtrace);
}
*backtrace = g_string_new(NULL);
*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
@@ -377,8 +412,8 @@ static int process_corefile(GString **backtrace)
*/
if (errorstr != NULL) {
telem_log(LOG_ERR, "%s", errorstr);
g_string_append_printf(header, "Error: %s", errorstr);
g_string_prepend(*backtrace, header->str);
nc_string_append_printf(header, "Error: %s", errorstr);
nc_string_prepend(*backtrace, header->str);
send_data(backtrace, error_severity, error_class);
}
goto fail;
@@ -389,69 +424,93 @@ fail:
return -1;
}
static bool in_clr_build(gchar *fullpath)
static bool startswith(const char *full, const char *prefix)
{
while (*prefix) {
if (*prefix != *full) {
return false;
}
full++;
prefix++;
}
return true;
}
static bool in_clr_build(char *fullpath)
{
// Global override for privacy filters
if (access(TM_PRIVACY_FILTERS_OVERRIDE, F_OK) == 0) {
return false;
}
/*
* The build environment for Clear Linux packages is set up by 'mock',
* and the chroot in which rpmbuild builds the packages has this prefix.
*/
if (g_regex_match_simple("^[!/]builddir[!/]build[!/]BUILD[!/]",
fullpath, 0, 0) == TRUE) {
if (startswith(fullpath, "/builddir/build/BUILD/") ||
startswith(fullpath, "!builddir!build!BUILD!")) {
return true;
}
return false;
}
static bool filter_binaries(gchar *fullpath)
static bool is_banned_path(char *fullpath)
{
// Anything outside of /usr/, or in /usr/local/, we consider third-party
if (g_regex_match_simple("^[!/]usr[!/]", fullpath, 0, 0) == FALSE) {
return false;
} else if (g_regex_match_simple("^[!/]usr[!/]local[!/]", fullpath,
0, 0) == TRUE) {
// Global override for privacy filters
if (access(TM_PRIVACY_FILTERS_OVERRIDE, F_OK) == 0) {
return false;
}
return true;
// Anything outside of /usr/, or in /usr/local/, we consider third-party
if (!startswith(fullpath, "/usr/") &&
!startswith(fullpath, "!usr!")) {
return true;
}
if (startswith(fullpath, "/usr/local/") ||
startswith(fullpath, "!usr!local!")) {
return true;
}
return false;
}
static gchar *config_file = NULL;
static gchar *core_file = NULL;
static gchar *proc_path = NULL;
static gint signal_num = -1;
static gboolean version_p = FALSE;
static gboolean verbose = FALSE;
static char *core_file = NULL;
static char *proc_path = NULL;
static long int signal_num = -1;
static bool verbose = false;
static GOptionEntry options[] = {
{ "config-file", 'f', 0, G_OPTION_ARG_FILENAME, &config_file,
"Path to configuration file (not implemented yet)", NULL },
{ "core-file", 'c', 0, G_OPTION_ARG_FILENAME, &core_file,
"Path to core file to process", NULL },
{ "process-name", 'p', 0, G_OPTION_ARG_STRING, &proc_name,
"Name of process for crash report (required)", NULL },
{ "process-path", 'E', 0, G_OPTION_ARG_STRING, &proc_path,
"Absolute path of crashed process, with ! or / delimiters", NULL },
{ "signal", 's', 0, G_OPTION_ARG_INT, &signal_num,
"Signal number that crashed the process", NULL },
{ "version", 'V', 0, G_OPTION_ARG_NONE, &version_p,
"Print the program version", NULL },
{ "verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose,
"Print the crash probe payload to stdout", NULL },
{ NULL }
static const struct option prog_opts[] = {
{ "help", no_argument, 0, 'h' },
{ "config-file", required_argument, 0, 'f' },
{ "core-file", required_argument, 0, 'c' },
{ "process-name", required_argument, 0, 'p' },
{ "process-path", required_argument, 0, 'E' },
{ "signal", required_argument, 0, 's' },
{ "version", no_argument, 0, 'V' },
{ "verbose", no_argument, 0, 'v' },
{ 0, 0, 0, 0 }
};
static void free_glib_strings(void)
static void print_help(void)
{
if (core_file) {
g_free(core_file);
}
if (proc_name) {
g_free(proc_name);
}
if (proc_path) {
g_free(proc_path);
}
printf("Usage:\n");
printf(" crashprobe [OPTIONS] - collect data from core files\n");
printf("\n");
printf("Help Options:\n");
printf(" -h, --help Show help options\n");
printf("\n");
printf("Application Options:\n");
printf(" -f, --config_file Specify a configuration file other than default\n");
printf(" -c, --core-file Path to core file to process\n");
printf(" -p, --process-name Name of process for crash report (required)\n");
printf(" -E, --process-path Absolute path of crashed process, with ! or / delimiters\n");
printf(" -s, --signal Signal number that crashed the process\n");
printf(" -V, --version Print the program version\n");
printf(" -v, --verbose Print the crash payload to stdout\n");
printf("\n");
}
int main(int argc, char **argv)
@@ -459,9 +518,7 @@ int main(int argc, char **argv)
Elf *e_core = NULL;
int ret = EXIT_FAILURE;
int core_fd = STDIN_FILENO;
GString *backtrace = NULL;
GError *error = NULL;
GOptionContext *context;
nc_string *backtrace = NULL;
if (fcntl(STDERR_FILENO, F_GETFL) < 0) {
// redirect stderr to avoid bad things to happen with
@@ -477,46 +534,58 @@ int main(int argc, char **argv)
drop_privs();
context = g_option_context_new("- collect data from core files");
g_option_context_add_main_entries(context, options, NULL);
g_option_context_set_translate_func(context, NULL, NULL, NULL);
if (!g_option_context_parse(context, &argc, &argv, &error)) {
g_print("Failed to parse options: %s\n", error->message);
exit(EXIT_FAILURE);
}
int opt;
if (version_p) {
g_print(PACKAGE_VERSION "\n");
exit(EXIT_SUCCESS);
while ((opt = getopt_long(argc, argv, "hf:c:p:E:s:Vv", prog_opts, NULL)) != -1) {
switch (opt) {
case 'h':
print_help();
goto success;
case 'V':
printf(PACKAGE_VERSION "\n");
goto success;
case 'f':
if (tm_set_config_file(optarg) != 0) {
telem_log(LOG_ERR, "Configuration file"
" path not valid\n");
exit(EXIT_FAILURE);
}
break;
case 'c':
core_file = strdup(optarg);
break;
case 'p':
proc_name = strdup(optarg);
break;
case 'E':
proc_path = strdup(optarg);
break;
case 's':
errno = 0;
char *endptr = NULL;
signal_num = strtol(optarg, &endptr, 10);
if (errno != 0) {
telem_perror("Failed to convert signal number");
goto fail;
}
if (endptr && *endptr != '\0') {
telem_log(LOG_ERR, "Invalid signal number. Must be an integer\n");
goto fail;
}
break;
case 'v':
verbose = true;
break;
}
}
if (!proc_name) {
g_print("Missing required -p option. See --help output\n");
printf("Missing required -p option. See --help output\n");
exit(EXIT_FAILURE);
}
if (proc_path && in_clr_build(proc_path)) {
telem_log(LOG_NOTICE, "Ignoring core (from mock build)\n");
backtrace = g_string_new("Crash from Clear package build\n");
if (!send_data(&backtrace, unknown_severity, clr_build_class)) {
goto fail;
}
goto success;
}
if (proc_path && !filter_binaries(proc_path)) {
telem_log(LOG_NOTICE, "Ignoring core (third-party binary)\n");
backtrace = g_string_new("Crash from third party\n");
if (!send_data(&backtrace, unknown_severity, unknown_class)) {
goto fail;
}
goto success;
}
if (core_file) {
core_fd = open(core_file, O_RDONLY);
if (core_fd == -1) {
@@ -543,37 +612,54 @@ int main(int argc, char **argv)
goto fail;
}
} else {
g_print("Cannot process core file. Use the -c option,"
" or pass the core file on stdin.\n");
printf("Cannot process core file. Use the -c option,"
" or pass the core file on stdin.\n");
goto fail;
}
}
if (proc_path && in_clr_build(proc_path)) {
telem_log(LOG_NOTICE, "Ignoring core (from mock build)\n");
backtrace = nc_string_dup("Crash from Clear package build\n");
if (!send_data(&backtrace, unknown_severity, clr_build_class)) {
goto fail;
}
goto success;
}
if (proc_path && is_banned_path(proc_path)) {
telem_log(LOG_NOTICE, "Ignoring core (third-party binary)\n");
backtrace = nc_string_dup("Crash from third party\n");
if (!send_data(&backtrace, unknown_severity, unknown_class)) {
goto fail;
}
goto success;
}
elf_version(EV_CURRENT);
if (prepare_corefile(&e_core, core_fd) < 0) {
goto fail;
}
header = g_string_new(NULL);
g_string_printf(header, "Process: %s\nPID: %u\n",
proc_path ? replace_exclamations(proc_path) : proc_name,
(unsigned int)core_for_pid);
header = nc_string_dup_printf("Process: %s\nPID: %u\n",
proc_path ? replace_exclamations(proc_path) : proc_name,
(unsigned int)core_for_pid);
if (signal_num >= 0) {
g_string_append_printf(header, "Signal: %d\n", signal_num);
}
if (process_corefile(&backtrace) < 0) {
goto fail;
nc_string_append_printf(header, "Signal: %ld\n", signal_num);
}
/* On Clear Linux OS, missing symbols may appear if automatic debuginfo
* downloads are still in flight. So if any missing symbols appear on
* the first run (indicated by the presence of "??? - ["), wait 10
* seconds and try again.
* seconds and try again. Also retry if errors occur in the first run.
*/
if (strstr(backtrace->str, "??? - [")) {
if ((process_corefile(&backtrace) < 0) || (strstr(backtrace->str, "??? - ["))) {
sleep(10);
if (prepare_corefile(&e_core, core_fd) < 0) {
@@ -585,7 +671,12 @@ int main(int argc, char **argv)
}
}
g_string_prepend(backtrace, header->str);
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)) {
goto fail;
@@ -600,22 +691,20 @@ success:
ret = EXIT_SUCCESS;
fail:
free_glib_strings();
if (context) {
g_option_context_free(context);
}
free(core_file);
free(proc_name);
free(proc_path);
if (header) {
g_string_free(header, TRUE);
nc_string_free(header);
}
if (backtrace) {
g_string_free(backtrace, TRUE);
nc_string_free(backtrace);
}
if (errorstr) {
g_free(errorstr);
free(errorstr);
}
if (d_core) {
@@ -630,6 +719,12 @@ fail:
close(core_fd);
}
// Remove the core file by default, except when the --core-file option
// is specified.
if (!core_file) {
unlink(temp_core);
}
return ret;
}
+133 -35
View File
@@ -1,7 +1,7 @@
/*
* This program is part of the Clear Linux Project
*
* Copyright 2015 Intel Corporation
* Copyright 2015-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
@@ -20,62 +20,155 @@
#include <string.h>
#include <sys/stat.h>
#include <getopt.h>
#include <locale.h>
#include <sys/sysinfo.h>
#include <dirent.h>
#include "common.h"
#include "telemetry.h"
#include "config.h"
#include "log.h"
void print_usage(char *prog)
#define PAYLOAD_LOCALE 0x01
#define PAYLOAD_UPTIME 0x02
#define PAYLOAD_BUNDLES 0x04
static void print_usage(char *prog)
{
printf("%s: Usage\n", prog);
printf(" -f, --config_file Configuration file. This overides the other parameters\n");
printf(" -H, --heartbeat Create a \"heartbeat\" record instead\n");
printf(" -h, --help Display this help message\n");
printf(" -l, --locale Include locale in the record\n");
printf(" -u, --uptime Include uptime in the record\n");
printf(" -b, --bundles Include installed bundles in the record\n");
printf(" -V, --version Print the program version\n");
}
static long get_uptime(void)
{
struct sysinfo s_info;
if (sysinfo(&s_info) == 0) {
return s_info.uptime;
}
return 0;
}
static int nodots(const struct dirent *dp)
{
return (dp->d_name[0] != '.');
}
static char *create_payload(unsigned options)
{
int n, buflen;
char *ppos, *payload;
payload = calloc(sizeof(char), MAX_PAYLOAD_LENGTH);
if (payload == NULL) {
return NULL;
}
ppos = payload;
buflen = MAX_PAYLOAD_LENGTH;
n = snprintf(ppos, (size_t)buflen, "hello\n");
buflen -= n, ppos += n;
if (options & PAYLOAD_LOCALE) {
n = snprintf(ppos, (size_t)buflen, "LC_ALL: %s\n", setlocale(LC_ALL, NULL));
buflen -= n, ppos += n;
}
if (options & PAYLOAD_UPTIME) {
n = snprintf(ppos, (size_t)buflen, "uptime: %ld\n", get_uptime());
buflen -= n, ppos += n;
}
if (options & PAYLOAD_BUNDLES) {
int num_bundles, i;
struct dirent **entries;
num_bundles = scandir("/usr/share/clear/bundles", &entries, nodots, alphasort);
if (num_bundles < 0) {
telem_perror("scandir failed");
free(payload);
return NULL;
}
n = snprintf(ppos, (size_t)buflen, "\nBundles (%d):\n", num_bundles);
buflen -= n, ppos += n;
for (i = 0; i < num_bundles; i++) {
n = snprintf(ppos, (size_t)buflen, "%s\n", entries[i]->d_name);
/* Test if truncated output */
if (n >= buflen) {
int lastix = MAX_PAYLOAD_LENGTH-1;
payload[lastix] = '\0';
payload[lastix-1] = '.';
payload[lastix-2] = '.';
payload[lastix-3] = '.';
payload[lastix-4] = '\n';
break;
}
buflen -= n, ppos += n;
}
for (i= 0; i < num_bundles; i++) {
free(entries[i]);
}
free(entries);
}
return payload;
}
int main(int argc, char **argv)
{
uint32_t severity = 1;
uint32_t payload_version = 1;
char classification[30] = "org.clearlinux/hello/world";
struct telem_ref *tm_handle = NULL;
char *payload2;
char *payload;
char *str;
int ret;
unsigned payload_options = 0;
// Following vars are for arg parsing.
int c;
char *cfile = NULL;
int opt_index = 0;
int ret = 0;
struct option opts[] = {
{ "cfile", 1, NULL, 'f' },
{ "config_file", 1, NULL, 'f' },
{ "heartbeat", 0, NULL, 'H' },
{ "help", 0, NULL, 'h' },
{ "version", 0, NULL, 'V' },
{ "locale", 0, NULL, 'l' },
{ "uptime", 0, NULL, 'u' },
{ "bundles", 0, NULL, 'b' },
{ NULL, 0, NULL, 0 }
};
while ((c = getopt_long(argc, argv, "f:hHV", opts, &opt_index)) != -1) {
while ((c = getopt_long(argc, argv, "f:hHVlub", opts, &opt_index)) != -1) {
switch (c) {
case 'f':
if (asprintf(&cfile, "%s", (char *)optarg) < 0) {
exit(EXIT_FAILURE);
if (tm_set_config_file(optarg) != 0) {
telem_log(LOG_ERR, "Configuration file"
" path not valid\n");
exit(EXIT_FAILURE);
}
struct stat buf;
ret = stat(cfile, &buf);
/* check if the file exists and is a regular file */
if (ret == -1 || !S_ISREG(buf.st_mode)) {
exit(EXIT_FAILURE);
}
tm_set_config_file(cfile);
break;
case 'H':
str = "org.clearlinux/heartbeat/ping";
memcpy(classification, str, strlen(str));
memcpy(classification, str, strlen(str) + 1);
break;
case 'l':
payload_options |= PAYLOAD_LOCALE;
break;
case 'u':
payload_options |= PAYLOAD_UPTIME;
break;
case 'b':
payload_options |= PAYLOAD_BUNDLES;
break;
case 'h':
print_usage(argv[0]);
@@ -88,34 +181,39 @@ int main(int argc, char **argv)
}
}
if (asprintf(&payload2, "hello\n") < 0) {
setlocale(LC_ALL, "");
payload = create_payload(payload_options);
if (payload == NULL) {
exit(EXIT_FAILURE);
}
if ((ret = tm_create_record(&tm_handle, severity, classification,
payload_version)) < 0) {
printf("Failed to create record: %s\n", strerror(-ret));
ret = 1;
goto fail;
telem_log(LOG_ERR, "Failed to create record: %s\n",
strerror(-ret));
ret = EXIT_FAILURE;
goto done;
}
if ((ret = tm_set_payload(tm_handle, payload2)) < 0) {
printf("Failed to set record payload: %s\n", strerror(-ret));
ret = 1;
goto fail;
if ((ret = tm_set_payload(tm_handle, payload)) < 0) {
telem_log(LOG_ERR, "Failed to set record payload: %s\n",
strerror(-ret));
ret = EXIT_FAILURE;
goto done;
}
free(payload2);
free(payload);
if ((ret = tm_send_record(tm_handle)) < 0) {
printf("Failed to send record to daemon: %s\n", strerror(-ret));
ret = 1;
goto fail;
telem_log(LOG_ERR, "Failed to send record to daemon: %s\n",
strerror(-ret));
ret = EXIT_FAILURE;
goto done;
} else {
printf("Successfully sent record to daemon.\n");
ret = 0;
ret = EXIT_SUCCESS;
}
fail:
done:
tm_free_record(tm_handle);
tm_handle = NULL;
+136 -78
View File
@@ -1,7 +1,7 @@
/*
* This program is part of the Clear Linux Project
*
* Copyright 2015 Intel Corporation
* Copyright 2015-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
@@ -15,6 +15,7 @@
*/
#define _GNU_SOURCE
#include <getopt.h>
#include <poll.h>
#include <stdbool.h>
#include <stdio.h>
@@ -22,8 +23,6 @@
#include <string.h>
#include <unistd.h>
#include <glib.h>
/* Certain static analysis tools do not understand GCC's __INCLUDE_LEVEL__
* macro; the conditional definition below is used to fix the build with
* systemd's _sd-common.h, included by the public systemd headers, which rely on
@@ -39,10 +38,11 @@
#include "config.h"
#include "log.h"
#include "telemetry.h"
#include "probe.h"
#include "nica/nc-string.h"
#define BOOT_ID_LEN 33
static GString *payload = NULL;
static nc_string *payload = NULL;
static uint32_t severity = 2;
static uint32_t payload_version = 1;
static char error_class[30] = "org.clearlinux/journal/error";
@@ -61,12 +61,11 @@ static inline void tm_journal_match_err(int ret)
static void add_to_payload(const void *data, size_t length)
{
if (payload != NULL) {
g_string_append_printf(payload, "%.*s", (int)length,
(char *)data);
nc_string_append_printf(payload, "%.*s\n", (int)length,
(char *)data);
} else {
payload = g_string_new(NULL);
g_string_printf(payload, "%.*s", (int)length,
(char *)data);
payload = nc_string_dup_printf("%.*s\n", (int)length,
(char *)data);
}
}
@@ -77,16 +76,17 @@ static bool send_data(char *class)
if ((ret = tm_create_record(&handle, severity, class,
payload_version)) < 0) {
telem_log(LOG_ERR, "Failed to create record: %s",
telem_log(LOG_ERR, "Failed to create record: %s\n",
strerror(-ret));
goto fail;
}
gchar *payload_str = g_string_free(payload, FALSE);
char *payload_str = strdup(payload->str);
nc_string_free(payload);
payload = NULL;
if ((ret = tm_set_payload(handle, (char *)payload_str)) < 0) {
telem_log(LOG_ERR, "Failed to set payload: %s", strerror(-ret));
telem_log(LOG_ERR, "Failed to set payload: %s\n", strerror(-ret));
free(payload_str);
tm_free_record(handle);
goto fail;
@@ -95,7 +95,7 @@ static bool send_data(char *class)
free(payload_str);
if ((ret = tm_send_record(handle)) < 0) {
telem_log(LOG_ERR, "Failed to send record: %s", strerror(-ret));
telem_log(LOG_ERR, "Failed to send record: %s\n", strerror(-ret));
tm_free_record(handle);
goto fail;
}
@@ -122,6 +122,16 @@ static int read_new_entries(sd_journal *journal)
add_to_payload(data, length);
// For now, we send one record per log message, in case the
// there is a large backlog of messages and we exceed the
// payload size limit (8KB). And ignore errors, hoping that it's
// a transient problem.
if (!send_data(error_class)) {
telem_log(LOG_ERR, "Failed to send data. Ignoring.\n");
return num_entries;
}
num_entries++;
}
@@ -148,19 +158,13 @@ static bool process_existing_entries(sd_journal *journal)
return false;
}
if (!read_new_entries(journal)) {
ret = read_new_entries(journal);
if (ret < 0) {
return false;
}
if (!payload) {
telem_log(LOG_DEBUG, "No existing entries found\n");
} else if (ret == 0) {
return true;
}
if (!send_data(error_class)) {
return false;
}
return true;
}
@@ -185,6 +189,33 @@ static bool get_boot_id(char **data)
return true;
}
#define JOURNAL_MATCH(data) \
do { \
r = sd_journal_add_match(journal, data, 0); \
if (r < 0) { \
tm_journal_match_err(r); \
return false; \
} \
} while (0);
#define JOURNAL_AND \
do { \
r = sd_journal_add_conjunction(journal); \
if (r < 0) { \
tm_journal_match_err(r); \
return false; \
} \
} while (0);
#define JOURNAL_OR \
do { \
r = sd_journal_add_disjunction(journal); \
if (r < 0) { \
tm_journal_match_err(r); \
return false; \
} \
} while (0);
static bool add_filters(sd_journal *journal)
{
char *data = NULL;
@@ -194,29 +225,47 @@ static bool add_filters(sd_journal *journal)
return false;
}
r = sd_journal_add_match(journal, data, 0);
if (r < 0) {
tm_journal_match_err(r);
return false;
}
/* The semantics of how journal entry matching works is described in
* detail in sd_journal_add_match(3).
*
* When the privacy filter override is enabled, the matches declared
* here correspond to this logical expression:
*
* BOOTID && ((P0 || P1 || P2 || P3) || EXITED)
*
* Otherwise, the expression is:
*
* BOOTID && EXITED
*
* BOOTID is short for _BOOT_ID=VAL, where VAL is the boot ID for the
* current boot. P0, P1, etc stand for PRIORITY=0, etc. And EXITED is
* short for EXIT_CODE=exited.
*/
JOURNAL_MATCH(data);
free(data);
JOURNAL_AND;
r = sd_journal_add_match(journal, "SYSLOG_IDENTIFIER=crashprobe", 0);
if (r < 0) {
tm_journal_match_err(r);
return false;
// Filter messages with the four highest log levels, all indicating
// errors, but only when the privacy filters override is in effect;
// because the log messages contain arbitrary strings, and this probe
// does not yet keep a whitelist of allowed patterns or a blacklist of
// banned patterns.
if (access(TM_PRIVACY_FILTERS_OVERRIDE, F_OK) == 0) {
JOURNAL_MATCH("PRIORITY=0");
JOURNAL_MATCH("PRIORITY=1");
JOURNAL_MATCH("PRIORITY=2");
JOURNAL_MATCH("PRIORITY=3");
JOURNAL_OR;
}
r = sd_journal_add_match(journal, "PRIORITY=3", 0);
if (r < 0) {
tm_journal_match_err(r);
return false;
}
// Only set for service-level error conditions
JOURNAL_MATCH("EXIT_CODE=exited");
return true;
}
static bool process_journal(void)
static bool process_journal(bool process_existing)
{
int r;
@@ -230,8 +279,14 @@ static bool process_journal(void)
return false;
}
if (!process_existing_entries(journal)) {
return false;
if (process_existing) {
if (!process_existing_entries(journal)) {
return false;
}
} else {
if ((sd_journal_seek_tail(journal) < 0) || (sd_journal_previous(journal) < 0)) {
return false;
}
}
struct pollfd pfd;
@@ -258,71 +313,74 @@ static bool process_journal(void)
} else if (r < 0) {
return false;
}
if (!send_data(error_class)) {
return false;
}
}
}
}
}
static gchar *config_file = NULL;
static gboolean version_p = FALSE;
static GOptionEntry options[] = {
{ "config-file", 'f', 0, G_OPTION_ARG_FILENAME, &config_file,
"Path to configuration file (not implemented yet)", NULL },
{ "version", 'V', 0, G_OPTION_ARG_NONE, &version_p,
"Print the program version", NULL },
{ NULL }
static const struct option prog_opts[] = {
{ "help", no_argument, 0, 'h' },
{ "config-file", required_argument, 0, 'f' },
{ "version", no_argument, 0, 'V' },
{ "tail", no_argument, 0, 't' },
{ 0, 0, 0, 0 }
};
static void free_glib_strings(void)
static void print_help(void)
{
if (config_file) {
g_free(config_file);
}
printf("Usage:\n");
printf(" journalprobe [OPTIONS] - collect data from systemd journal\n");
printf("\n");
printf("Help Options:\n");
printf(" -h, --help Show help options\n");
printf("\n");
printf("Application Options:\n");
printf(" -f, --config_file Specify a configuration file other than default\n");
printf(" -t, --tail Don't process entries already in the journal\n");
printf(" -V, --version Print the program version\n");
printf("\n");
}
int main(int argc, char **argv)
{
int ret = EXIT_FAILURE;
GError *error = NULL;
GOptionContext *context;
int opt;
bool process_existing = true;
context = g_option_context_new("- collect data from systemd journal");
g_option_context_add_main_entries(context, options, NULL);
g_option_context_set_translate_func(context, NULL, NULL, NULL);
if (!g_option_context_parse(context, &argc, &argv, &error)) {
g_print("Failed to parse options: %s\n", error->message);
goto fail;
while ((opt = getopt_long(argc, argv, "hf:Vt", prog_opts, NULL)) != -1) {
switch (opt) {
case 'h':
print_help();
goto success;
case 'V':
printf(PACKAGE_VERSION "\n");
goto success;
case 'f':
if (tm_set_config_file(optarg) != 0) {
telem_log(LOG_ERR, "Configuration file"
" path not valid\n");
exit(EXIT_FAILURE);
}
break;
case 't':
process_existing = false;
break;
}
}
if (version_p) {
g_print(PACKAGE_VERSION "\n");
goto success;
}
if (!process_journal()) {
if (!process_journal(process_existing)) {
goto fail;
}
success:
ret = EXIT_SUCCESS;
fail:
free_glib_strings();
if (journal) {
sd_journal_close(journal);
}
if (context) {
g_option_context_free(context);
}
if (payload) {
g_string_free(payload, TRUE);
nc_string_free(payload);
}
return ret;
+50 -40
View File
@@ -1,12 +1,28 @@
/*
* This program is part of the Clear Linux Project
*
* Copyright © 2015-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 <sys/klog.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <malloc.h>
#include <signal.h>
#include "log.h"
#include "oops_parser.h"
@@ -21,54 +37,48 @@ int main(void)
int log_size = 0;
char *bufp = NULL;
size_t buflen = 0;
int bytes = 0;
int loopcount = 0;
oops_parser_init(write_oops_to_file);
oops_parser_init(klog_process_oops_msgs);
// Allocate space to write the oops file to
if (allocate_filespace() < 0) {
telem_log(LOG_ERR, "%s\n", strerror(errno));
return 1;
}
// Signal Handling to close and unlink empty files
if (signal (SIGINT, signal_handler) == SIG_ERR) {
// Signal Handling to terminate the probe.
if (signal (SIGINT, signal_handler_fail) == SIG_ERR) {
telem_log(LOG_ERR, "Error handling interrupt signal\n");
}
if (signal (SIGTERM, signal_handler) == SIG_ERR) {
if (signal (SIGTERM, signal_handler_success) == SIG_ERR) {
telem_log(LOG_ERR, "Error handling terminating signal\n");
}
while (1) {
// 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\n");
return 1;
}
buflen = (size_t)log_size;
if (loopcount++ > 0 && buflen > MAX_BUF)
buflen = MAX_BUF;
// Gets the contents of the kernel ring buffer
bufp = (char *)calloc(buflen, sizeof(char));
malloc_trim(0);
bytes = klogctl(SYSLOG_ACTION_READ, bufp, log_size);
if (bytes < 0) {
telem_log(LOG_ERR, "Cannot read contents of kernel ring buffer: %s\n", strerror(errno));
return 1;
}
// Splits the buffer into separate lines with /0 terminating
split_buf_by_line(bufp, (size_t)bytes);
free(bufp);
// Gets the size of the kernel ring buffer
log_size = klogctl(SYSLOG_ACTION_SIZE_BUFFER, NULL, 0);
if (log_size < 0) {
telem_perror("Cannot read size of kernel ring buffer");
return 1;
}
buflen = (size_t)log_size;
if (buflen > MAX_BUF) {
buflen = MAX_BUF;
}
// Gets the contents of the kernel ring buffer
bufp = (char *)malloc(buflen);
while (1) {
int bytes_read;
malloc_trim(0);
memset(bufp, 0, buflen);
bytes_read = klogctl(SYSLOG_ACTION_READ, bufp, (int)buflen);
if (bytes_read < 0) {
telem_perror("Cannot read contents of kernel ring buffer");
return 1;
}
klog_process_buffer(bufp, (size_t)bytes_read);
}
// Not reached
free(bufp);
return 0;
}
+98 -70
View File
@@ -1,7 +1,7 @@
/*
* This program is part of the Clear Linux Project
*
* Copyright 2015 Intel Corporation
* Copyright © 2015-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
@@ -15,33 +15,71 @@
*/
#define _GNU_SOURCE
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/klog.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <glib.h>
#include <sys/stat.h>
#include "common.h"
#include "log.h"
#include "oops_parser.h"
#include "klog_scanner.h"
#include "telemetry.h"
#include "nica/nc-string.h"
#define MAX_OOPS_MSG_LENGTH 1024
#define FILE_PATH KERNELOOPSDIR "/kerneloopsXXXXXX"
static bool oops_processed = false;
static int fd;
char filename[] = FILE_PATH;
int last_line_length;
static uint32_t version = 1;
static bool send_data(char *backtrace, char *class, uint32_t severity)
{
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",
strerror(-ret));
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);
return false;
}
if ((ret = tm_send_record(handle)) < 0) {
telem_log(LOG_ERR, "Failed to send record: %s", strerror(-ret));
tm_free_record(handle);
return false;
}
tm_free_record(handle);
return true;
}
int klog_process_buffer(char *bufp, int bytes)
{
// Splits the buffer into separate lines with /0 terminating
split_buf_by_line(bufp, (size_t)bytes);
return 0;
}
bool oops_written = false;
void split_buf_by_line(char *bufp, int bytes)
{
char *start;
char *eol;
size_t linelength = 0;
oops_processed = false;
while (bytes > 0) {
eol = memchr(bufp, '\n', (size_t)bytes);
@@ -58,87 +96,77 @@ void split_buf_by_line(char *bufp, int bytes)
if (bytes >= 0) {
start[linelength] = '\0';
parse_single_line(start, linelength);
if (oops_processed == true) {
telem_debug("oops_processed detected !\n");
break;
}
linelength = 0;
}
}
}
int allocate_filespace()
void klog_process_oops_msgs(struct oops_log_msg *msg)
{
int allocate_disk = 0;
strcpy(filename, FILE_PATH);
fd = mkstemp(filename);
if (fchmod(fd, 0644) == -1) {
telem_log(LOG_ERR, "Error while changing permissions for file %s: %s\n",
filename, strerror(errno));
}
if (fd == -1) {
telem_log(LOG_ERR, "fd error: %s\n", strerror(errno));
return -1;
}
// Allocates disk space to write kernel oops to
allocate_disk = fallocate(fd, 0, 0, MAX_OOPS_MSG_LENGTH);
if (allocate_disk != 0) {
telem_log(LOG_ERR, "Error allocating disk space: %s\n", strerror(errno));
return -1;
}
return 0;
}
void write_oops_to_file(struct oops_log_msg *msg)
{
ssize_t writer;
size_t linelength;
size_t linelength, size;
char *line;
char *contents = NULL;
nc_string *payload;
// pass 1: calculate buffer size
size = 0;
for (int i = 0; i < msg->length; i++) {
size += strlen(msg->lines[i]) + 1;
}
contents = malloc(size);
if (!contents) {
telem_log(LOG_ERR, "Call to malloc failed");
oops_processed = true;
return;
}
// pass 2: copy strings into the buffer
size_t done = 0;
char *bp = contents;
for (int i = 0; i < msg->length; i++) {
//Add the newline character to the end of each line
line = msg->lines[i];
linelength = strlen(line);
line[linelength] = '\n';
linelength++;
// Copy the line (without the terminating NULL)
memcpy(bp, line, linelength);
bp[linelength] = '\n';
done += linelength + 1;
bp = contents + done;
}
// Write each line of oops to file
writer = write(fd, line, linelength);
if (writer == -1) {
telem_log(LOG_ERR, "Error writing buffer to file: %s\n", strerror(errno));
exit(EXIT_FAILURE);
struct oops_log_msg oops_msg;
if (handle_entire_oops(contents, (long)size, &oops_msg)) {
#ifdef DEBUG
telem_debug("DEBUG: Raw oops message:\n");
for (int i = 0; i < oops_msg.length; i++) {
telem_log(LOG_DEBUG, "%s\n", oops_msg.lines[i]);
}
#endif
payload = parse_payload(&oops_msg);
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);
} else {
/* File does no contain an oops! */
telem_log(LOG_ERR, "Did not find an oops in the oops directory\n");
}
telem_log(LOG_INFO, "Oops written to file\n");
oops_written = true;
close(fd);
// Allocate more space to prepare for another oops
if (allocate_filespace() < 0) {
telem_log(LOG_ERR, "%s\n", strerror(errno));
exit(EXIT_FAILURE);
}
free(contents);
oops_processed = true;
}
bool get_oops_written(void)
void signal_handler_success(int signum)
{
return oops_written;
exit(EXIT_SUCCESS);
}
char *get_file_name(void)
{
return filename;
}
__attribute__((destructor))
void clean_up(void)
{
// Unlink empty file and close any open file
unlink(filename);
close(fd);
}
void signal_handler(int signum)
void signal_handler_fail(int signum)
{
exit(EXIT_FAILURE);
}
+17 -21
View File
@@ -1,7 +1,7 @@
/*
* This program is part of the Clear Linux Project
*
* Copyright 2015 Intel Corporation
* Copyright © 2015-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,45 +17,41 @@
#pragma once
#include "oops_parser.h"
/**
* Process the buffer and send it to the backend
*
* @param *bufp A pointer to a buffer containing the bytes read
* from the kernel ring buffer
* @param bytes Number of bytes read from ring buffer into bufp
*
* @return 0 on succes, -1 on failure
*
*/
int klog_process_buffer(char *bufp, int bytes);
/**
* Splits the buffer into individual lines
*
* @param *bufp A pointer to a buffer containing the bytes read
* from the kernel ring buffer
* @param bytes Bytes read from ring buffer into bufp
* @param bytes Number of bytes read from ring buffer into bufp
*
*/
void split_buf_by_line(char *bufp, int bytes);
/**
* Allocates memory for oops message to write to file
*
* @return 0 on succes, -1 on failure
*/
int allocate_filespace(void);
/**
* Writes the oops message to tmp file
* Process the oops message and send it to the backend
*
* @param msg Struct that contains oops message lines and linecount
*
*/
void write_oops_to_file(struct oops_log_msg *msg);
void klog_process_oops_msgs(struct oops_log_msg *msg);
/**
* Signal handler for cleanup purposes
*
* @param signum An integer for the signal type
*
*/
void signal_handler(int signum);
/**
* For Unit Tests Purposes
* Determines whether an oops has been written to a file
*
* @return true if oops is written, false if not
*/
bool get_oops_written(void);
void signal_handler_fail(int signum);
void signal_handler_success(int signum);
/* vi: set ts=8 sw=8 sts=4 et tw=80 cino=(0: */
+34 -46
View File
@@ -4,25 +4,38 @@ bin_PROGRAMS += \
%D%/telem-record-gen \
%D%/klogscanner \
%D%/pstoreprobe \
%D%/oopsprobe \
%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
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 = \
$(AM_CFLAGS) \
$(GLIB_CFLAGS)
%C%_telem_record_gen_LDADD = $(top_builddir)/src/libtelemetry.la \
$(GLIB_LIBS)
$(AM_CFLAGS)
%C%_telem_record_gen_LDADD = $(top_builddir)/src/libtelemetry.la
%C%_telem_record_gen_LDFLAGS = \
$(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)
@@ -44,15 +57,15 @@ endif
%C%_crashprobe_SOURCES = \
%D%/crash_probe.c
%D%/crash_probe.c \
src/nica/nc-string.c \
%D%/probe.h
%C%_crashprobe_CFLAGS = \
$(AM_CFLAGS) \
$(GLIB_CFLAGS)
$(AM_CFLAGS)
%C%_crashprobe_LDADD = \
$(top_builddir)/src/libtelemetry.la \
$(top_builddir)/src/libtelem-shared.la \
@ELFUTILS_LIBS@ \
$(GLIB_LIBS)
@ELFUTILS_LIBS@
%C%_crashprobe_LDFLAGS = \
$(AM_LDFLAGS) \
-pie
@@ -68,14 +81,13 @@ endif
%C%_pstoreprobe_SOURCES = \
%D%/pstore_probe.c \
src/nica/nc-string.c \
%D%/oops_parser.c
%C%_pstoreprobe_CFLAGS = \
$(AM_CFLAGS) \
$(GLIB_CFLAGS)
$(AM_CFLAGS)
%C%_pstoreprobe_LDADD = \
$(top_builddir)/src/libtelemetry.la \
$(top_builddir)/src/libtelem-shared.la \
$(GLIB_LIBS)
$(top_builddir)/src/libtelem-shared.la
if LOG_SYSTEMD
if HAVE_SYSTEMD_JOURNAL
@@ -91,25 +103,17 @@ endif
%D%/klog_scanner.c \
%D%/klog_scanner.h \
%D%/oops_parser.h \
src/nica/nc-string.c \
%D%/oops_parser.c
%C%_klogscanner_CFLAGS = \
$(AM_CFLAGS) \
$(GLIB_CFLAGS)
$(AM_CFLAGS)
%C%_klogscanner_LDADD = \
$(top_builddir)/src/libtelemetry.la \
$(GLIB_LIBS)
$(top_builddir)/src/libtelemetry.la
%C%_klogscanner_LDFLAGS = \
$(AM_LDFLAGS) \
-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)
@@ -118,25 +122,6 @@ if HAVE_SYSTEMD_JOURNAL
endif
endif
%C%_oopsprobe_SOURCES = \
%D%/oops_probe.c \
%D%/oops_parser.c
%C%_oopsprobe_CFLAGS = \
$(AM_CFLAGS) \
$(GLIB_CFLAGS)
%C%_oopsprobe_LDADD = \
$(top_builddir)/src/libtelemetry.la \
$(top_builddir)/src/libtelem-shared.la \
$(GLIB_LIBS)
if LOG_SYSTEMD
if HAVE_SYSTEMD_JOURNAL
%C%_oopsprobe_CFLAGS += \
$(SYSTEMD_JOURNAL_CFLAGS)
%C%_oopsprobe_LDADD += \
$(SYSTEMD_JOURNAL_LIBS)
endif
endif
if HAVE_SYSTEMD_JOURNAL
if HAVE_SYSTEMD_ID128
@@ -144,21 +129,24 @@ bin_PROGRAMS += \
%D%/journalprobe
%C%_journalprobe_SOURCES = \
src/nica/nc-string.c \
%D%/journal.c
%C%_journalprobe_CFLAGS = \
$(AM_CFLAGS) \
$(GLIB_CFLAGS) \
$(SYSTEMD_ID128_CFLAGS) \
$(SYSTEMD_JOURNAL_CFLAGS)
%C%_journalprobe_LDADD = \
$(top_builddir)/src/libtelemetry.la \
$(top_builddir)/src/libtelem-shared.la \
$(GLIB_LIBS) \
$(SYSTEMD_ID128_LIBS) \
$(SYSTEMD_JOURNAL_LIBS)
%C%_journalprobe_LDFLAGS = \
$(AM_LDFLAGS) \
-pie
if LOG_SYSTEMD
%C%_journalprobe_CFLAGS += $(SYSTEMD_JOURNAL_CFLAGS)
%C%_journalprobe_LDADD += $(SYSTEMD_JOURNAL_LIBS)
endif
endif
endif
+188 -76
View File
@@ -1,7 +1,7 @@
/*
* This program is part of the Clear Linux Project
*
* Copyright 2015 Intel Corporation
* Copyright 2015-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
@@ -21,9 +21,11 @@
#include <string.h>
#include <ctype.h>
#include <inttypes.h>
#include <unistd.h>
#include "oops_parser.h"
#include "log.h"
#include "probe.h"
#define NUM_TAINTED_FLAGS 16
@@ -148,11 +150,29 @@ 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,
},
};
int oops_patterns_cnt = sizeof(oops_patterns_arr) / sizeof(struct oops_pattern);
static int oops_patterns_cnt = sizeof(oops_patterns_arr) / sizeof(struct oops_pattern);
char *skip_log_level(char *line)
static char *skip_log_level(char *line)
{
char *start = line;
@@ -167,7 +187,7 @@ char *skip_log_level(char *line)
return start;
}
char *skip_timestamp(char *line)
static char *skip_timestamp(char *line)
{
char *start = line;
@@ -182,7 +202,7 @@ char *skip_timestamp(char *line)
return start;
}
char *skip_spaces(char *line)
static char *skip_space(char *line)
{
char *start = line;
@@ -192,7 +212,17 @@ char *skip_spaces(char *line)
return start;
}
bool starts_with(const char *line, const char *line_end, const char *substr)
static char *skip_spaces(char *line)
{
char *start = line;
while (*start && isspace(*start)) {
start++;
}
return start;
}
static bool starts_with(const char *line, const char *line_end, const char *substr)
{
size_t substrlen = strlen(substr);
@@ -202,7 +232,7 @@ bool starts_with(const char *line, const char *line_end, const char *substr)
return false;
}
bool str_starts_with_casei(const char *line, const char *substr)
static bool str_starts_with_casei(const char *line, const char *substr)
{
size_t len = strlen(substr);
@@ -212,7 +242,7 @@ bool str_starts_with_casei(const char *line, const char *substr)
return false;
}
bool pattern_matches(char *line, char *line_end, struct oops_pattern *pattern)
static bool pattern_matches(char *line, char *line_end, struct oops_pattern *pattern)
{
if (pattern->is_regex) {
return (regexec(&(pattern->regex), line, 0, NULL, 0) == 0);
@@ -221,7 +251,7 @@ bool pattern_matches(char *line, char *line_end, struct oops_pattern *pattern)
}
}
void init_pattern_regex(void)
static void init_pattern_regex(void)
{
for (int i = 0; i < oops_patterns_cnt; i++) {
struct oops_pattern *pattern = &oops_patterns_arr[i];
@@ -232,7 +262,7 @@ void init_pattern_regex(void)
}
}
void free_pattern_regex(void)
static void free_pattern_regex(void)
{
for (int i = 0; i < oops_patterns_cnt; i++) {
struct oops_pattern *pattern = &oops_patterns_arr[i];
@@ -249,7 +279,6 @@ bool handle_entire_oops(char *buf, long size, struct oops_log_msg *msg)
int i;
msg->length = 0;
init_pattern_regex();
line_end = memchr(buf, '\n', (size_t)size);
if (line_end == NULL) {
@@ -306,12 +335,12 @@ void oops_msg_cleanup(struct oops_log_msg *msg)
free(msg->lines[i]);
}
msg->length = 0;
free_pattern_regex();
}
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;
@@ -321,7 +350,7 @@ void oops_parser_init(oops_handler_t handler)
init_pattern_regex();
}
void handle_msg_end(void)
static void handle_msg_end(void)
{
for (int i = 0; i < oops_msg.length; i++) {
free(oops_msg.lines[i]);
@@ -335,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;
@@ -344,7 +386,7 @@ void parse_single_line(char *line, size_t size)
line_end = line + size;
start = skip_log_level(line);
start = skip_timestamp(start);
start = skip_spaces(start);
start = skip_space(start);
struct oops_pattern *pattern;
if (oops_msg.length == 0) {
@@ -354,41 +396,50 @@ 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) {
if (starts_with(start, line_end, " [<") ||
starts_with(start, line_end, " <") || //case : <EOI> [<fff73..]
starts_with(start, line_end, "Call Trace:")) {
} 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:")) {
in_stack_dump = true;
}
} else if (in_stack_dump) {
if (!(starts_with(start, line_end, " [") || starts_with(start, line_end, " <")) ||
// In Linux 4.10+, the oops format changed, so we look
// for a single leading space character to indicate a
// stack frame line.
if (!(starts_with(start, line_end, " ")) ||
(strlen(start) < 8) ||
(strstr(start, "Code:") != NULL) ||
(strstr(start, "Instruction Dump::") != NULL)) {
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 */
@@ -411,7 +462,6 @@ void parse_single_line(char *line, size_t size)
}
}
}
}
struct stack_frame {
@@ -422,12 +472,23 @@ struct stack_frame {
struct stack_frame *next;
};
void stack_frame_append(struct stack_frame **head, struct stack_frame **tail, char *start)
static void stack_frame_append(struct stack_frame **head, struct stack_frame **tail, char *start)
{
/*
* Format is as :
* Format is:
*
* [<ffffffffa1002128>] do_one_initcall+0xb8/0x1e0
* [<ffffffffa118784a>] ? __vunmap+0x9a/0x100
*
* OR
*
* <IRQ> [<ffffffff816606a8>] dump_stack+0x19/0x1b
*
* OR (in 4.10+)
*
* do_one_initcall+0xb8/0x1e0
* ? __vunmap+0x9a/0x100
*
*/
struct stack_frame *frame = NULL;
@@ -436,18 +497,22 @@ void stack_frame_append(struct stack_frame **head, struct stack_frame **tail, ch
if (!start) {
return;
}
start = start + 2;
// Skip leading spaces
start = skip_spaces(start);
//case: <IRQ> [<ffffffff816606a8>] dump_stack+0x19/0x1b
if (!strncmp(start, "IRQ>", 4) || !strncmp(start, "NMI>", 4) ||
!strncmp(start, "EOI>", 4) || !strncmp(start, "<EOE>>", 6)) {
while (*start && *start != '[') {
if (!strncmp(start, "<IRQ>", 5) || !strncmp(start, "<NMI>", 5) ||
!strncmp(start, "<EOI>", 5) || !strncmp(start, "<<EOE>>", 7)) {
while (*start && !isspace(*start)) {
start++;
}
if (*start == '\0') {
return;
}
start++;
// Skip intervening space
start = skip_spaces(start);
}
frame = malloc(sizeof(struct stack_frame));
@@ -462,21 +527,31 @@ void stack_frame_append(struct stack_frame **head, struct stack_frame **tail, ch
*tail = frame;
}
if (*start == '<') {
// Skip memory address parsing if the address is absent (as in Linux 4.10+).
if (!strncmp(start, "[", 1)) {
start++;
frame->addr = (uint64_t)strtoll(start, (char **)&start, 16);
if (*start == '<') {
start++;
frame->addr = (uint64_t)strtoll(start, (char **)&start, 16);
} else {
frame->addr = 0;
}
// Skip up to the space after the closing bracket
while (*start && !isspace(*start)) {
start++;
}
if (*start == '\0') {
return;
}
// Skip intervening space
start = skip_spaces(start);
} else {
frame->addr = 0;
}
/* Skip up to the space after the closing bracket */
while (*start && !isspace(*start)) {
start++;
}
if (*start) {
start++;
}
offset_ptr = strchr(start, '+');
end = offset_ptr ? offset_ptr : (start + strlen(start));
frame->function = strndup(start, (size_t)(end - start));
@@ -496,12 +571,16 @@ void stack_frame_append(struct stack_frame **head, struct stack_frame **tail, ch
frame->module = "kernel";
}
void stack_frame_free(struct stack_frame **head)
static void stack_frame_free(struct stack_frame **head)
{
struct stack_frame *frame;
while (*head != NULL) {
frame = (*head)->next;
// First free memory allocation for function name
if ((*head)->function) {
free((*head)->function);
}
free(*head);
*head = frame;
}
@@ -509,11 +588,11 @@ 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
*/
void parse_kernel_cpu_line(char *line, char **kernel_version, char **tainted)
static void parse_kernel_cpu_line(char *line, char **kernel_version, char **tainted)
{
char *end = NULL;
@@ -586,7 +665,7 @@ static struct reg_s *reg_head = NULL;
*
*/
void parse_registers(char *line)
static void parse_registers(char *line)
{
uint64_t value;
struct reg_s *reg_entry = NULL;
@@ -699,18 +778,19 @@ void parse_registers(char *line)
}
void append_registers_to_bt(GString **backtrace)
static void append_registers_to_bt(nc_string **backtrace)
{
struct reg_s *reg_entry = reg_head;
while (reg_entry != NULL) {
#ifdef DEBUG
g_string_append_printf(*backtrace, "Register %s: %" PRIx64 "\n", reg_entry->reg_name,
reg_entry->reg_value);
#else
g_string_append_printf(*backtrace, "Register %s: %s\n", reg_entry->reg_name,
reg_entry->reg_value ? "Non-zero" : "Zero");
#endif
// Global override for privacy filters
if (access(TM_PRIVACY_FILTERS_OVERRIDE, F_OK) == 0) {
nc_string_append_printf(*backtrace, "Register %s: %" PRIx64 "\n", reg_entry->reg_name,
reg_entry->reg_value);
} else {
nc_string_append_printf(*backtrace, "Register %s: %s\n", reg_entry->reg_name,
reg_entry->reg_value ? "Non-zero" : "Zero");
}
reg_entry = reg_entry->next;
}
@@ -721,25 +801,36 @@ void append_registers_to_bt(GString **backtrace)
}
}
GString *parse_backtrace(struct oops_log_msg *msg)
static nc_string *parse_backtrace(struct oops_log_msg *msg)
{
struct stack_frame *head = NULL, *tail = NULL, *elem = NULL;
//int in_stack_dump = 0;
char *line = NULL;
GString *backtrace = NULL;
nc_string *backtrace = NULL;
int frame_counter = 1;
char *modules = NULL, *kernel_version = NULL, *tainted = NULL;
// Since lines are processed from last to first, the stack trace lines
// will appear first.
bool in_trace = true;
for (int i = msg->length - 1; i > 0; i--) {
/* Check if this line is part of a stack trace */
line = msg->lines[i];
if (starts_with(line, line + strlen(line), " [") ||
starts_with(line, line + strlen(line), " <")) {
if (in_trace && starts_with(line, line + strlen(line), " ")) {
stack_frame_append(&head, &tail, line);
continue;
}
// We've reached the end of the stack trace
if (starts_with(line, line + strlen(line), "Call Trace:")) {
in_trace = false;
}
// Stack trace lines must be consecutive, so if this point is
// reached, we're outside the trace boundary.
in_trace = false;
if (str_starts_with_casei(line, "Modules linked in: ")) {
modules = line + strlen("Modules linked in: ");
continue;
@@ -747,29 +838,38 @@ GString *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;
}
parse_registers(line);
}
backtrace = g_string_new(NULL);
backtrace = nc_string_dup("");
if (kernel_version) {
g_string_append_printf(backtrace, "Kernel Version : %s\n", kernel_version);
nc_string_append_printf(backtrace, "Kernel Version : %s\n", kernel_version);
free(kernel_version);
}
if (tainted) {
g_string_append_printf(backtrace, "Tainted : %s\n", tainted);
nc_string_append_printf(backtrace, "Tainted : %s\n", tainted);
free(tainted);
}
if (modules) {
g_string_append_printf(backtrace, "Modules : %s\n", modules);
nc_string_append_printf(backtrace, "Modules : %s\n", modules);
}
if (head) {
g_string_append_printf(backtrace, "Backtrace :\n");
nc_string_append_printf(backtrace, "Backtrace :\n");
}
if (reg_head) {
@@ -777,24 +877,37 @@ GString *parse_backtrace(struct oops_log_msg *msg)
}
for (elem = head; elem != NULL; elem = elem->next, frame_counter++) {
g_string_append_printf(backtrace, "#%d %s - [%s]\n", frame_counter,
elem->function ? elem->function : "???",
elem->module);
nc_string_append_printf(backtrace, "#%d %s - [%s]\n", frame_counter,
elem->function ? elem->function : "???",
elem->module);
}
stack_frame_free(&head);
return backtrace;
}
GString *parse_payload(struct oops_log_msg *msg)
static void append_payload(nc_string *payload, struct oops_log_msg *msg)
{
GString *payload, *backtrace;
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]);
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);
}
payload = g_string_new("Crash Report:\n");
g_string_append_printf(payload, "Reason: %s\n", msg->lines[0]);
backtrace = parse_backtrace(msg);
g_string_append (payload, backtrace->str);
g_string_free(backtrace, true);
return payload;
}
@@ -807,4 +920,3 @@ GString *parse_payload(struct oops_log_msg *msg)
*/
/* vi: set ts=8 sw=8 sts=4 et tw=80 cino=(0: */
+2 -3
View File
@@ -17,7 +17,7 @@
#define MAX_LINES 100
#include <glib.h>
#include "nica/nc-string.h"
#include <stdbool.h>
#include <regex.h>
@@ -63,7 +63,7 @@ void oops_parser_init(oops_handler_t handler);
void parse_single_line(char *line, size_t size);
/* Parses a payload from an oops msg*/
GString *parse_payload(struct oops_log_msg *msg);
nc_string *parse_payload(struct oops_log_msg *msg);
/* Given an entire oops log, verifies that the log is an oops log and
* extracts the lines into an oops struct.
@@ -74,4 +74,3 @@ bool handle_entire_oops(char *buf, long size, struct oops_log_msg *msg);
void oops_msg_cleanup(struct oops_log_msg *msg);
/* vi: set ts=8 sw=8 sts=4 et tw=80 cino=(0: */
-229
View File
@@ -1,229 +0,0 @@
/*
* 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.
*/
#define _GNU_SOURCE
#include <stdio.h>
#include <sys/types.h>
#include <glib.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <glib.h>
#include <sys/inotify.h>
#include <poll.h>
#include "oops_parser.h"
#include "log.h"
#include "telemetry.h"
char *oops_dir_path = KERNELOOPSDIR;
static uint32_t version = 1;
static bool send_data(char *backtrace, char *class, uint32_t severity)
{
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",
strerror(-ret));
return false;
}
if ((ret = tm_set_payload(handle, backtrace)) < 0) {
telem_log(LOG_ERR, "Failed to set payload: %s", strerror(-ret));
tm_free_record(handle);
return false;
}
if ((ret = tm_send_record(handle)) < 0) {
telem_log(LOG_ERR, "Failed to send record: %s", strerror(-ret));
tm_free_record(handle);
return false;
}
tm_free_record(handle);
return true;
}
void handle_oops_file(const char *fname)
{
int ret;
FILE *fp = NULL;
char *filename = NULL, *contents = NULL;
long sz;
size_t size, bytes_read;
GString *payload;
ret = asprintf(&filename, "%s/%s", oops_dir_path, fname);
if (ret == -1) {
exit(EXIT_FAILURE);
}
#ifdef DEBUG
printf("File to be handled: %s\n", filename);
#endif
fp = fopen(filename, "r");
if (fp == NULL) {
telem_log(LOG_ERR, "Failed to open oops log file %s: %s", fname, strerror(errno));
goto file_end;
}
if (fseek(fp, 0, SEEK_END) == -1) {
telem_log(LOG_ERR, "Failed to seek file %s: %s\n", fname, strerror(errno));
goto file_end;
}
sz = ftell(fp);
if (sz == -1L) {
telem_perror("ftell failed");
goto file_end;
}
/* If file is empty */
if (sz == 0L) {
telem_perror("File is empty\n");
goto file_end;
}
if (fseek(fp, 0, SEEK_SET) < 0) {
telem_log(LOG_ERR, "Fseek failed for file %s:%s\n", fname, strerror(errno));
goto file_end;
}
size = (size_t)sz;
contents = malloc(size);
if (!contents) {
telem_log(LOG_ERR, "Call to malloc failed");
goto file_end;
}
memset(contents, 0, size);
bytes_read = fread(contents, sizeof(char), size, fp);
if (bytes_read < size) {
telem_log(LOG_ERR, "Error while reading file %s:%s\n", fname, strerror(errno));
goto file_end;
}
struct oops_log_msg oops_msg;
if (handle_entire_oops(contents, (long)size, &oops_msg)) {
#ifdef DEBUG
printf("Raw message:\n");
for (int i = 0; i < oops_msg.length; i++) {
printf("%s\n", oops_msg.lines[i]);
}
#endif
payload = parse_payload(&oops_msg);
#ifdef DEBUG
printf("Payload Parsed :%s\n", payload->str);
#endif
oops_msg_cleanup(&oops_msg);
send_data(payload->str, (char *)oops_msg.pattern->classification, (uint32_t)oops_msg.pattern->severity);
g_string_free(payload, true);
} else {
/* File does no contain an oops! */
telem_log(LOG_ERR, "Did not find an oops in the oops directort\n");
}
file_end:
if (contents) {
free(contents);
}
if (fp) {
fclose(fp);
}
if (unlink(filename) == -1) {
telem_log(LOG_ERR, "Failed to unlink %s: %s\n", filename, strerror(errno));
}
free(filename);
}
void handle_inotify_event(const struct inotify_event *event)
{
if (!event) {
telem_perror("Null event received");
return;
}
if (event->len == 0) {
telem_perror("inotify event received with no file.");
return;
}
if (event->mask & IN_CLOSE_WRITE) {
if (event->mask & IN_ISDIR) {
return;
}
#ifdef DEBUG
telem_log(LOG_DEBUG, "New file written to the oops location: %s\n", event->name);
#endif
handle_oops_file(event->name);
}
}
int main(int argc, char **argv)
{
int inotify_fd;
char buf[4096];
char *ptr = NULL;
ssize_t len;
const struct inotify_event *event;
inotify_fd = inotify_init();
if (inotify_fd == -1) {
telem_log(LOG_ERR, "Failed to create inotify instance: %s\n", strerror(errno));
exit(EXIT_FAILURE);
}
if (inotify_add_watch(inotify_fd, oops_dir_path, IN_CLOSE_WRITE) == -1) {
telem_perror("Error adding watch to the inotify instance");
}
while (1) {
len = read(inotify_fd, buf, sizeof buf);
if (len <= 0) {
telem_perror("error reading event");
continue;
}
if (len < sizeof(struct inotify_event)) {
telem_perror("Incomplete event received");
continue;
}
telem_log(LOG_INFO, "Got inotify event/s\n");
/* Loop over all events in the buffer */
for (ptr = buf; ptr < buf + len;
ptr += sizeof(struct inotify_event) + event->len) {
event = (const struct inotify_event *)ptr;
/* handle the event */
handle_inotify_event(event);
}
}
return 0;
}
/* vi: set ts=8 sw=8 sts=4 et tw=80 cino=(0: */
+25
View File
@@ -0,0 +1,25 @@
/*
* 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.
*/
#pragma once
/**
* Existence of the opt-in-no-privacy-filters file disables certain privacy
* filters enforced in telemetry probes.
*/
#define TM_PRIVACY_FILTERS_OVERRIDE "/etc/telemetrics/opt-in-no-privacy-filters"
/* vi: set ts=8 sw=8 sts=4 et tw=80 cino=(0: */
+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);
}
+61 -27
View File
@@ -12,11 +12,11 @@
* A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*/
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <dirent.h>
#include <glib.h>
#include <string.h>
#include <errno.h>
#include <assert.h>
@@ -26,6 +26,7 @@
#include "log.h"
#include "telemetry.h"
#include "oops_parser.h"
#include "nica/hashmap.h"
char *pstore_dump_path = PSTOREDIR;
@@ -61,20 +62,18 @@ static bool send_data(char *backtrace, char *class, uint32_t severity)
void handle_complete_oops_message(struct oops_log_msg *msg)
{
GString *payload;
nc_string *payload;
#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);
g_string_free(payload, true);
nc_string_free(payload);
}
void handle_crash_dump(char *dump, size_t size)
@@ -100,7 +99,7 @@ void handle_crash_dump(char *dump, size_t size)
char *read_contents(char *filename, size_t *bytes)
{
gchar *dump_file;
char *dump_file = NULL;
FILE *fp = NULL;
char *contents = NULL;
long sz = 0;
@@ -108,7 +107,10 @@ char *read_contents(char *filename, size_t *bytes)
char *hdr_end;
size_t hdr_len;
dump_file = g_strconcat(pstore_dump_path, "/", filename, NULL);
if (asprintf(&dump_file, "%s/%s", pstore_dump_path, filename) < 0) {
goto end;
}
fp = fopen(dump_file, "r");
if (fp == NULL) {
telem_log(LOG_ERR, "Failed to open pstore dump file %s:%s\n", dump_file, strerror(errno));
@@ -170,7 +172,7 @@ end:
fclose(fp);
}
g_free(dump_file);
free(dump_file);
return contents;
}
@@ -194,22 +196,55 @@ 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;
struct dirent *entry;
struct chunk_list *elem, *head;
GHashTableIter iter;
gpointer key, value;
NcHashmapIter iter;
void *key, *value;
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
* key -> count, value->head of linked list of chunks
*/
GHashTable *hash = g_hash_table_new(g_int_hash, g_int_equal);
NcHashmap *hash = nc_hashmap_new(nc_simple_hash, nc_simple_compare);
pstore_dir = opendir(pstore_dump_path);
if (pstore_dir == NULL) {
@@ -229,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;
}
@@ -247,24 +286,19 @@ int main(int argc, char **argv)
elem->contents = read_contents(entry->d_name, &(elem->size));
elem->next = NULL;
gpointer head = g_hash_table_lookup(hash, (void *)(&elem->count));
void *head = nc_hashmap_get(hash, NC_HASH_KEY(elem->count));
if (head != NULL) {
elem->next = (struct chunk_list *)head;
}
gint *key = g_new(gint, 1);
*key = elem->count;
g_hash_table_insert(hash, key, elem);
nc_hashmap_put(hash, NC_HASH_KEY(elem->count), elem);
}
closedir(pstore_dir);
g_hash_table_iter_init (&iter, hash);
while (g_hash_table_iter_next (&iter, &key, &value)) {
#ifdef DEBUG
printf("Count in the hash: %d\n", *((int *)key));
#endif
nc_hashmap_iter_init(hash, &iter);
while (nc_hashmap_iter_next(&iter, (void **)&key, (void **)&value)) {
telem_debug("DEBUG: Count in the hash: %d\n", NC_UNHASH_KEY(key));
head = (struct chunk_list *)value;
elem = head;
+257 -143
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
@@ -15,6 +15,8 @@
*/
#define _GNU_SOURCE
#include <ctype.h>
#include <getopt.h>
#include <poll.h>
#include <stdbool.h>
#include <stdio.h>
@@ -22,155 +24,230 @@
#include <string.h>
#include <unistd.h>
#include <glib.h>
#include <string.h>
#include <stdint.h>
#include "config.h"
#include "log.h"
#include "common.h"
#include "telemetry.h"
static gchar *config_file = NULL;
static gboolean version_p = FALSE;
static guint severity = 1;
static gchar *opt_class = NULL;
static gchar *opt_payload = NULL;
static guint payload_version = 1;
static gchar *opt_payload_file = NULL;
static uint32_t severity = 1;
static char *opt_class = NULL;
static char *opt_payload = NULL;
static uint32_t payload_version = 1;
static char *opt_payload_file = NULL;
static char *opt_event_id = NULL;
static bool opt_echo = false;
static bool opt_nopost = false;
static GOptionEntry options[] = {
{ "config-file", 'f', 0, G_OPTION_ARG_FILENAME, &config_file,
"Path to configuration file (not implemented yet)", NULL },
{ "version", 'V', 0, G_OPTION_ARG_NONE, &version_p,
"Print the program version", NULL },
{ "severity", 's', 0, G_OPTION_ARG_INT, &severity,
"Severity level (1-4) - (default 1)", NULL },
{ "class", 'c', 0, G_OPTION_ARG_STRING, &opt_class,
"Classification level_1/level_2/level_3", NULL },
{ "payload", 'p', 0, G_OPTION_ARG_STRING, &opt_payload,
"Record body (max size = 8k)", NULL },
{ "payload-file", 'P', 0, G_OPTION_ARG_STRING, &opt_payload_file,
"File to read payload from", NULL },
{ "record-version", 'R', 0, G_OPTION_ARG_INT, &payload_version,
"Version number for format of payload (default 1)", NULL },
{ NULL }
static const struct option prog_opts[] = {
{ "help", no_argument, 0, 'h' },
{ "echo", no_argument, 0, 'o' },
{ "no-post", no_argument, 0, 'n' },
{ "version", no_argument, 0, 'V' },
{ "severity", required_argument, 0, 's' },
{ "class", required_argument, 0, 'c' },
{ "payload", required_argument, 0, 'p' },
{ "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 }
};
const unsigned int count_chars(const char *check, const char character)
static void print_help(void)
{
unsigned int count = 0U;
if (check == NULL) {
return count;
}
for (; *check != '\0'; ++check) {
if (*check == character) {
++count;
printf("Usage:\n");
printf(" telem-record-gen [OPTIONS] - create and send a custom telemetry record\n");
printf("\n");
printf("Help Options:\n");
printf(" -h, --help Show help options\n");
printf("\n");
printf("Application Options:\n");
printf(" -V, --version Print the program version\n");
printf(" -s, --severity Severity level (1-4) - (default 1)\n");
printf(" -c, --class Classification level_1/level_2/level_3 (required)\n");
printf(" -p, --payload Record body (max size = 8k) (required)\n");
printf(" -P, --payload-file File to read payload from\n");
printf(" -R, --record-version Version number for format of payload (default 1)\n");
printf(" -e, --event-id Event id to use in the record\n");
printf(" -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");
}
static bool parse_options(int argc, char **argv)
{
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:onf:", prog_opts, NULL)) != -1) {
switch (opt) {
case 'h':
print_help();
exit(EXIT_SUCCESS);
case 'V':
printf(PACKAGE_VERSION "\n");
exit(EXIT_SUCCESS);
case 's':
errno = 0;
tmp = strtoul(optarg, &endptr, 10);
if (errno != 0) {
telem_perror("Failed to convert severity number");
goto fail;
}
if (endptr && *endptr != '\0') {
telem_log(LOG_ERR, "Invalid severity number. Must be an integer\n");
goto fail;
}
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':
errno = 0;
tmp = strtoul(optarg, &endptr, 10);
if (errno != 0) {
telem_perror("Failed to convert record-version number");
goto fail;
}
if (endptr && *endptr != '\0') {
telem_log(LOG_ERR, "Invalid record-version number. Must be an integer\n");
goto fail;
}
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;
}
break;
case 'o':
opt_echo = true;
break;
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;
}
}
return count;
}
static void free_glib_strings(void)
{
if (config_file) {
g_free(config_file);
}
if (opt_class) {
g_free(opt_class);
}
if (opt_payload) {
g_free(opt_payload);
}
}
int parse_options(int argc, char **argv)
{
int ret = 0;
GError *error = NULL;
GOptionContext *context;
context = g_option_context_new(
"- create and send a custom telemetry record\n");
g_option_context_add_main_entries(context, options, NULL);
g_option_context_set_translate_func(context, NULL, NULL, NULL);
if (!g_option_context_parse(context, &argc, &argv, &error)) {
g_print("Failed to parse options: %s\n", error->message);
goto fail;
}
ret = 1;
ret = true;
fail:
if (context) {
g_option_context_free(context);
}
return ret;
}
int validate_opts(void)
static bool validate_opts(void)
{
size_t i;
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;
}
i = strlen(opt_class);
if ((i == 0) || (i > 120)) {
len = strlen(opt_class);
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;
}
if (!g_str_is_ascii(opt_class)) {
fprintf(stderr, "Error: Non-ascii characters detected "
"in classification - aborting\n");
return ret;
for (int c = 0; c < len; c++) {
if (isascii(opt_class[c]) == 0) {
fprintf(stderr, "Error: Non-ascii characters detected "
"in classification - aborting\n");
return ret;
}
}
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");
return ret;
}
return 1;
}
int allocate_payload_buffer(char **payload)
{
int ret = 0;
*payload = (char *)malloc(sizeof(gchar) * MAX_PAYLOAD_SIZE);
if (*payload == NULL) {
goto out1;
/* Event id */
if (opt_event_id) {
size_t result = 0;
if ((result = strlen(opt_event_id)) != EVENT_ID_LEN) {
fprintf(stderr, "Error: event_id length %zu it should have %d characters\n", result, EVENT_ID_LEN);
return ret;
}
if ((result = strspn(opt_event_id, alphab)) != EVENT_ID_LEN) {
fprintf(stderr, "Error: event_id contains invalid character '%c' at position %zu, valid characters are %s\n",
opt_event_id[result], result, alphab);
return ret;
}
}
*payload = memset(*payload, 0, MAX_PAYLOAD_SIZE);
ret = 1;
out1:
return ret;
return true;
}
int get_payload_from_file(char **payload)
static bool get_payload_from_file(char **payload)
{
int ret = 1;
bool ret = false;
FILE *fp = NULL;
size_t bytes_in = 0;
@@ -182,15 +259,15 @@ int get_payload_from_file(char **payload)
goto out;
}
bytes_in = fread(*payload, sizeof(gchar),
MAX_PAYLOAD_SIZE - 1, fp);
bytes_in = fread(*payload, 1,
MAX_PAYLOAD_LENGTH - 1, fp);
/* if fread fails */
if (bytes_in == 0 && ferror(fp) != 0) {
ret = 0;
goto out;
}
ret = true;
out:
if (fp) {
fclose(fp);
@@ -199,25 +276,27 @@ out:
}
void get_payload_from_opt(char **payload)
static void get_payload_from_opt(char **payload)
{
size_t len = 0;
len = strlen(opt_payload);
if (len >= MAX_PAYLOAD_SIZE) {
len = MAX_PAYLOAD_SIZE - 1;
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;
bytes_in = fread(*payload, sizeof(gchar), MAX_PAYLOAD_SIZE - 1, stdin);
bytes_in = fread(*payload, 1, MAX_PAYLOAD_LENGTH - 1, stdin);
if (bytes_in == MAX_PAYLOAD_SIZE - 1) {
if (bytes_in == MAX_PAYLOAD_LENGTH - 1) {
/* Throw away the rest of stdin */
while (EOF != (c = fgetc(stdin))) {
continue;
@@ -226,60 +305,95 @@ 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;
}
return ret;
}
static int instanciate_record(struct telem_ref **t_ref, char *payload)
{
int ret = 0;
if ((ret = tm_create_record(t_ref, (uint32_t)severity,
opt_class, payload_version)) < 0) {
goto out1;
}
if (opt_event_id) {
if ((ret = tm_set_event_id(*t_ref, opt_event_id)) < 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;
struct telem_ref *t_ref = NULL;
int ret = 0;
if (tm_create_record(&t_ref, (uint32_t)severity,
opt_class, payload_version) < 0) {
goto out1;
if ((ret = instanciate_record(&t_ref, payload)) < 0) {
goto out;
}
if (tm_set_payload(t_ref, payload) < 0) {
goto out2;
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;
}
out:
tm_free_record(t_ref);
return ret;
}
static int print_record(char *payload)
{
struct telem_ref *t_ref = NULL;
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);
}
if (tm_send_record(t_ref) >= 0) {
ret = 1;
}
out2:
tm_free_record(t_ref);
out1:
return ret;
}
@@ -292,11 +406,6 @@ int main(int argc, char **argv)
goto fail;
}
if (version_p) {
g_print(PACKAGE_VERSION "\n");
goto success;
}
if (!validate_opts()) {
goto fail;
}
@@ -305,17 +414,22 @@ int main(int argc, char **argv)
goto fail;
}
if (!send_record(payload)) {
if (opt_echo == true) {
print_record(payload);
}
if (opt_nopost == false && send_record(payload) != 0) {
goto fail;
}
success:
ret = EXIT_SUCCESS;
fail:
free_glib_strings();
free(opt_class);
free(opt_payload);
free(opt_event_id);
free(payload);
return ret;
}
/* vi: set ts=8 sw=8 sts=4 et tw=80 cino=(0: */
+46
View File
@@ -0,0 +1,46 @@
/*
* This program is part of the Clear Linux Project
*
* Copyright 2018 Intel Corporation
*
* This program is free software; you can redistribute it and/or modify it under
* the terms and conditions of the GNU Lesser General Public License, as
* published by the Free Software Foundation; either version 2.1 of the License,
* or (at your option) any later version.
*
* This program is distributed in the hope it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*/
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include "log.h"
#include "common.h"
int delete_record_by_id(char *record_id)
{
int ret = 0;
char *record_path = NULL;
ret = asprintf(&record_path, "%s/%s", RECORD_RETENTION_DIR, record_id);
if (ret == -1) {
return 1;
}
ret = unlink(record_path);
if (ret == -1) {
telem_perror("Error deleting saved record");
}
free(record_path);
return 0;
}
/* vi: set ts=8 sw=8 sts=4 et tw=80 cino=(0: */
+27
View File
@@ -0,0 +1,27 @@
/*
* This program is part of the Clear Linux Project
*
* Copyright 2018 Intel Corporation
*
* This program is free software; you can redistribute it and/or modify it under
* the terms and conditions of the GNU Lesser General Public License, as
* published by the Free Software Foundation; either version 2.1 of the License,
* or (at your option) any later version.
*
* This program is distributed in the hope it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*/
#define _GNU_SOURCE
/**
* Delete record identified by record unique id
*
* @param record_id Unique identifier of entry
*
*/
int delete_record_by_id(char *record_id);
/* vi: set ts=8 sw=8 sts=4 et tw=80 cino=(0: */
+73 -21
View File
@@ -25,9 +25,10 @@
#include <limits.h>
#include <stdbool.h>
#include <errno.h>
#include <fcntl.h>
#include "spool.h"
#include "telemdaemon.h"
#include "telempostdaemon.h"
#include "log.h"
#include "configuration.h"
#include "util.h"
@@ -149,27 +150,37 @@ void process_spooled_record(const char *spool_dir, char *name,
}
(*records_processed)++;
ret = stat(record_name, &buf);
if (ret == -1) {
telem_perror("Unable to stat record in spool");
free(record_name);
return;
// Use file descriptor to mitigate TOCTOU
int fd = open(record_name, O_RDONLY | O_NOFOLLOW);
if (fd == -1) {
telem_perror("Unable to open record in spool");
goto exit;
}
if (fstat(fd, &buf) == -1) {
telem_perror("Unable to fstat record in spool");
close(fd);
goto exit;
}
/*
* If file is a regular file , if uid is diff than process uid
* or if mtime is greater than record expiry delete the file
* If file is a regular file, if uid is different than process uid,
* or if mtime is greater than record expiry, delete the file.
*/
if (record_expiry_config() == -1) {
telem_log(LOG_ERR, "Invalid record expiry value\n");
close(fd);
exit(EXIT_FAILURE);
}
if (!S_ISREG(buf.st_mode) ||
(current_time - buf.st_mtime > (record_expiry_config() * 60)) ||
(buf.st_uid != getuid())) {
(buf.st_uid != getuid())) {
unlink(record_name);
close(fd);
} else if (post_succeeded && *records_sent <= TM_SPOOL_MAX_SEND_RECORDS) {
close(fd);
transmit_spooled_record(record_name, &post_succeeded, buf.st_size);
if (!post_succeeded) {
@@ -194,6 +205,7 @@ void process_spooled_record(const char *spool_dir, char *name,
}
}
}
exit:
free(record_name);
}
@@ -202,9 +214,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 +230,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], strlen(header_name))) {
if (get_header(line, header_name, &headers[num_headers])) {
continue;
} else {
telem_log(LOG_ERR, "transmit_spooled_record: Incorrect"
@@ -242,8 +291,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 +302,7 @@ void transmit_spooled_record(char *record_path, bool *post_succeeded, long size)
goto read_error;
}
*post_succeeded = post_record_http(headers, payload, false);
*post_succeeded = post_record_http(headers, payload, cfg_file);
if (*post_succeeded) {
unlink(record_path);
}
@@ -267,9 +315,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);
}
+176 -407
View File
@@ -27,38 +27,25 @@
#include <fcntl.h>
#include <time.h>
#include <malloc.h>
#include <sys/uio.h>
#include "iorecord.h"
#include "telemdaemon.h"
#include "common.h"
#include "util.h"
#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);
daemon->nfds = 0;
daemon->pollfds = NULL;
daemon->client_head = head;
daemon->bypass_http_post_ts = 0;
daemon->is_spool_valid = false;
initialize_rate_limit(daemon);
daemon->current_spool_size = 0;
}
void initialize_rate_limit(TelemDaemon *daemon)
{
for (int i = 0; i < TM_RATE_LIMIT_SLOTS; i++) {
daemon->record_burst_array[i] = 0;
daemon->byte_burst_array[i] = 0;
}
daemon->rate_limit_enabled = rate_limit_enabled_config();
daemon->record_burst_limit = record_burst_limit_config();
daemon->record_window_length = record_window_length_config();
daemon->byte_burst_limit = byte_burst_limit_config();
daemon->byte_window_length = byte_window_length_config();
daemon->rate_limit_strategy = rate_limit_strategy_config();
daemon->machine_id_override = NULL;
}
client *add_client(client_list_head *client_head, int fd)
@@ -95,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);
@@ -106,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));
@@ -135,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);
@@ -149,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;
}
@@ -237,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
@@ -262,33 +274,102 @@ 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;
char *headers[NUM_HEADERS];
bool do_spool = false;
bool record_sent = false;
char *tok = NULL;
size_t header_size = 0;
size_t message_size = 0;
char *temp_headers = NULL;
char *msg;
char *body;
int current_minute = 0;
bool record_check_passed = true;
bool byte_check_passed = true;
bool record_burst_enabled = true;
bool byte_burst_enabled = true;
char *recordpath = NULL;
char *cfg_file = NULL;;
size_t cfg_info_size = 0;
uint8_t *buf;
/* Gets the current minute of time */
time_t temp = time(NULL);
struct tm *tm_s = localtime(&temp);
current_minute = tm_s->tm_min;
buf = cl->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);
/* 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);
@@ -297,7 +378,7 @@ void process_record(TelemDaemon *daemon, client *cl)
for (i = 0; i < NUM_HEADERS; i++) {
const char *header_name = get_header_name(i);
if (get_header(tok, header_name, &headers[i], strlen(header_name))) {
if (get_header(tok, header_name, &headers[i])) {
if (strcmp(header_name, TM_MACHINE_ID_STR) == 0) {
machine_id_replace(&headers[i], daemon->machine_id_override);
}
@@ -311,67 +392,15 @@ void process_record(TelemDaemon *daemon, client *cl)
/* TODO : check if the body is within the limits. */
body = msg + header_size;
if (inside_direct_spool_window(daemon, time(NULL))) {
telem_log(LOG_INFO, "process_record: delivering directly to spool\n");
spool_record(daemon, headers, body);
goto end;
}
if (daemon->record_window_length == -1 ||
daemon->byte_window_length == -1) {
telem_log(LOG_ERR, "Invalid value for window length\n");
/* Save record to stage */
ret = asprintf(&recordpath, "%s/XXXXXX", spool_dir_config());
if (ret == -1) {
telem_log(LOG_ERR, "Failed to allocate memory for record name in staging folder, aborting\n");
exit(EXIT_FAILURE);
}
/* Checks if entirety of rate limiting is enabled */
if (daemon->rate_limit_enabled) {
/* Checks whether record and byte bursts are enabled individually */
record_burst_enabled = burst_limit_enabled(daemon->record_burst_limit);
byte_burst_enabled = burst_limit_enabled(daemon->byte_burst_limit);
if (record_burst_enabled) {
record_check_passed = rate_limit_check(current_minute,
daemon->record_burst_limit, daemon->record_window_length,
daemon->record_burst_array, TM_RECORD_COUNTER);
}
if (byte_burst_enabled) {
byte_check_passed = rate_limit_check(current_minute,
daemon->byte_burst_limit, daemon->byte_window_length,
daemon->byte_burst_array, cl->size);
}
/* If both record and byte burst disabled, rate limiting disabled */
if (!record_burst_enabled && !byte_burst_enabled) {
daemon->rate_limit_enabled = false;
}
}
/* 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, true);
}
// Get rate-limit strategy
do_spool = spool_strategy_selected(daemon);
// Drop record
if (!record_sent && !do_spool) {
goto end;
}
// Spool Record
else if (!record_sent && do_spool) {
start_network_bypass(daemon);
telem_log(LOG_INFO, "process_record: initializing direct-spool window\n");
spool_record(daemon, headers, body);
} else {
/* Updates rate limiting arrays if record sent */
if (record_burst_enabled) {
rate_limit_update (current_minute, daemon->record_window_length,
daemon->record_burst_array, TM_RECORD_COUNTER);
}
if (byte_burst_enabled) {
rate_limit_update (current_minute, daemon->byte_window_length,
daemon->byte_burst_array, cl->size);
}
}
stage_record(recordpath, headers, body, cfg_file);
free(recordpath);
end:
free(temp_headers);
for (int k = 0; k < i; k++)
@@ -379,234 +408,6 @@ end:
return;
}
bool burst_limit_enabled(int64_t burst_limit)
{
return (burst_limit > -1) ? true : false;
}
bool rate_limit_check(int current_minute, int64_t burst_limit, int
window_length, size_t *array, size_t incValue)
{
size_t count = 0;
/* Variable to determine last minute allowed in burst window */
int window_start = (TM_RATE_LIMIT_SLOTS + (current_minute - window_length + 1))
% TM_RATE_LIMIT_SLOTS;
/* The modulo is not placed in the for loop inself, because if
* the current minute is less than the window start (from wrapping)
* the for loop will never be entered.
*/
/* Counts all elements within burst_window in array */
for (int i = window_start; i < (window_start + window_length); i++) {
count += array[i % TM_RATE_LIMIT_SLOTS];
if ((array[i % TM_RATE_LIMIT_SLOTS] + incValue) >= SIZE_MAX) {
/* Exceeds maximum size array can hold */
return false;
}
}
/* Include current record being processed to count */
count += incValue;
/* Determines whether the count has exceeded the limit or not */
return (count > burst_limit) ? false : true;
}
void rate_limit_update(int current_minute, int window_length, size_t *array,
size_t incValue)
{
/* Variable to determine the amount of zero'd out spots needed */
int blank_slots = (TM_RATE_LIMIT_SLOTS - window_length);
/* Update specified array depending on increment value */
array[current_minute] += incValue;
/* Zero out expired records for record array */
for (int i = current_minute + 1; i < (blank_slots + current_minute + 1); i++) {
array[i % TM_RATE_LIMIT_SLOTS] = 0;
}
}
bool spool_strategy_selected(TelemDaemon *daemon)
{
/* Performs action depending on strategy choosen */
return (strcmp(daemon->rate_limit_strategy, "spool") == 0) ? true : false;
}
bool inside_direct_spool_window(TelemDaemon *daemon, time_t current_time)
{
return (current_time < daemon->bypass_http_post_ts + 1800) ? true : false;
}
void start_network_bypass(TelemDaemon *daemon)
{
daemon->bypass_http_post_ts = time(NULL);
}
size_t write_callback(char *ptr, size_t size, size_t nmemb, void *userdata)
{
telem_log(LOG_DEBUG, "Received data:\n%s\n", ptr);
return size * nmemb;
}
bool post_record_http(char *headers[], char *body, bool spool)
{
CURL *curl;
int res = 0;
char *content = "Content-Type: application/text";
struct curl_slist *custom_headers = NULL;
char errorbuf[CURL_ERROR_SIZE];
long http_response = 0;
const char *cert_file = get_cainfo_config();
const char *tid_header = get_tidheader_config();
curl = curl_easy_init();
if (!curl) {
telem_log(LOG_ERR, "curl_easy_init(): Unable to start libcurl"
" easy session, exiting\n");
exit(EXIT_FAILURE);
/* TODO: check if memory needs to be released */
}
// Errors for any curl_easy_* functions will store nice error messages
// in errorbuf, so send log messages with errorbuf contents
curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errorbuf);
curl_easy_setopt(curl, CURLOPT_URL, server_addr_config());
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 5L);
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 10L);
curl_easy_setopt(curl, CURLOPT_POST, 1);
#ifdef DEBUG
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
#endif
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_USE_SSL, CURLUSESSL_TRY);
if (strlen(cert_file) > 0) {
if (access(cert_file, F_OK) != -1) {
curl_easy_setopt(curl, CURLOPT_CAINFO, cert_file);
telem_log(LOG_INFO, "cafile was set to %s\n", cert_file);
}
}
telem_log(LOG_DEBUG, "Executing curl operation...\n");
errorbuf[0] = 0;
res = curl_easy_perform(curl);
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_response);
if (res) {
size_t len = strlen(errorbuf);
if (len) {
telem_log(LOG_ERR, "Failed sending record: %s%s", errorbuf,
((errorbuf[len - 1] != '\n') ? "\n" : ""));
} else {
telem_log(LOG_ERR, "Failed sending record: %s\n",
curl_easy_strerror(res));
}
} else if (http_response != 201 && http_response != 200) {
/* 201 means the record was successfully created
* 200 is a generic "ok".
* TODO: Make list of white-listed response codes configurable?
*/
telem_log(LOG_ERR, "Encountered error %ld on the server\n",
http_response);
// We treat HTTP error codes the same as libcurl errors
res = 1;
} else {
telem_log(LOG_INFO, "Record sent successfully\n");
}
curl_slist_free_all(custom_headers);
curl_easy_cleanup(curl);
return res ? false : true;
}
void spool_record(TelemDaemon *daemon, char *headers[], char *body)
{
int ret = 0;
struct stat stat_buf;
int64_t max_spool_size = 0;
char *tmpbuf = NULL;
FILE *tmpfile = NULL;
int tmpfd = 0;
if (!daemon->is_spool_valid) {
/* If spool is not valid, simply drop the record */
return;
}
//check if the size is greater than 1 MB/spool max size
max_spool_size = spool_max_size_config();
if (max_spool_size != -1) {
telem_log(LOG_DEBUG, "Total size of spool dir: %ld\n", daemon->current_spool_size);
if (daemon->current_spool_size < 0) {
telem_log(LOG_ERR, "Error getting spool directory size: %s\n",
strerror(-(int)(daemon->current_spool_size)));
return;
} else if (daemon->current_spool_size >= (max_spool_size * 1024)) {
telem_log(LOG_INFO, "Spool dir full, dropping record\n");
return;
}
}
// create file with record
ret = asprintf(&tmpbuf, "%s/XXXXXX", spool_dir_config());
if (ret == -1) {
telem_log(LOG_ERR, "Failed to allocate memory for record name, aborting\n");
exit(EXIT_FAILURE);
}
tmpfd = mkstemp(tmpbuf);
if (tmpfd == -1) {
telem_perror("Error while creating temp file");
goto spool_err;
}
//fp = fopen(spool_dir_path, const char *mode);
tmpfile = fdopen(tmpfd, "a");
if (!tmpfile) {
telem_perror("Error opening temp file");
close(tmpfd);
if (unlink(tmpbuf)) {
telem_perror("Error deleting temp file");
}
goto spool_err;
}
// 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);
/* The stat should not fail here unless it is ENOMEM */
if (stat(tmpbuf, &stat_buf) == 0) {
daemon->current_spool_size += (stat_buf.st_blocks * 512);
}
spool_err:
free(tmpbuf);
return;
}
void add_pollfd(TelemDaemon *daemon, int fd, short events)
{
assert(daemon);
@@ -661,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);
@@ -671,7 +472,7 @@ bool get_machine_id(char *machine_id)
return true;
}
int machine_id_write(uint64_t upper, uint64_t lower)
int machine_id_write(char *new_id)
{
FILE *fp;
int ret = 0;
@@ -683,7 +484,7 @@ int machine_id_write(uint64_t upper, uint64_t lower)
goto file_error;
}
ret = fprintf(fp, "%.16" PRIx64 "%.16" PRIx64, upper, lower);
ret = fprintf(fp, "%s", new_id);
if (ret < 0) {
telem_perror("Unable to write to machine id file");
goto file_error;
@@ -700,49 +501,17 @@ file_error:
int generate_machine_id(void)
{
int frandom = -1;
struct stat stat_buf;
int ret = 0;
struct iovec random_id[2];
uint64_t random_id_upper;
uint64_t random_id_lower;
int result = -1;
int result = 0;
char *new_id = NULL;
ret = stat("/dev/urandom", &stat_buf);
if (ret == -1) {
telem_log(LOG_ERR, "Unable to stat urandom device\n");
if ((result = get_random_id(&new_id)) != 0) {
goto rand_err;
}
if (!S_ISCHR(stat_buf.st_mode)) {
telem_log(LOG_ERR, "/dev/urandom is not a character device file\n");
goto rand_err;
}
/* TODO : check for major and minor numbers to be extra sure ??
((stat_buffer.st_rdev == makedev(1, 8)) || (stat_buffer.st_rdev == makedev(1, 9))*/
frandom = open("/dev/urandom", O_RDONLY);
if (frandom < 0) {
telem_perror("Error opening /dev/urandom");
goto rand_err;
}
random_id[0].iov_base = &random_id_upper;
random_id[0].iov_len = 8;
random_id[1].iov_base = &random_id_lower;
random_id[1].iov_len = 8;
if (readv(frandom, random_id, 2) < 0) {
telem_perror("Error while reading /dev/urandom");
goto rand_err;
}
result = machine_id_write(random_id_upper, random_id_lower);
result = machine_id_write(new_id);
rand_err:
if (frandom >= 0) {
close(frandom);
}
free(new_id);
return result;
}
+2 -145
View File
@@ -54,23 +54,6 @@ typedef struct TelemDaemon {
size_t current_alloc;
/* client list head */
client_list_head client_head;
/* Time of last failed post */
time_t bypass_http_post_ts;
/* Rate limit record and byte arrays */
size_t record_burst_array[TM_RATE_LIMIT_SLOTS];
size_t byte_burst_array[TM_RATE_LIMIT_SLOTS];
/* Rate Limit Configurations */
bool rate_limit_enabled;
int64_t record_burst_limit;
int record_window_length;
int64_t byte_burst_limit;
int byte_window_length;
const char *rate_limit_strategy;
bool is_spool_valid;
long current_spool_size;
char *machine_id_override;
} TelemDaemon;
@@ -79,14 +62,7 @@ typedef struct TelemDaemon {
*
* @param daemon A pointer to the daemon structure.
*/
void initialize_daemon(TelemDaemon *daemon);
/**
* Initialize rate-limiting in the daemon
*
* @param daemon A pointer to the daemon structure
*/
void initialize_rate_limit(TelemDaemon *daemon);
void initialize_probe_daemon(TelemDaemon *daemon);
/**
* Add poll fd struct to the array of pollfds.
@@ -149,126 +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);
/**
* Check if burst limiting is enabled
*
* @param burst_limit Burst limit amount. If negative rate limiting
* is disabled
*
* @return true if rate limiting for burst_limit variable is enabled
*/
bool burst_limit_enabled(int64_t burst_limit);
/**
* Checks whether record does not exceed the burst threshold
*
* @param current_minute The current minute of time the record processed
* @param burst_limit Amount of records that can be passed within a window
* @param window_length Window of time burst limit refers to
* @param array Pointer to array that stores record or byte count
* @param incValue Amount to increase array slot by
*
* @return true if record does not exceed threshold, and false otherwise
*/
bool rate_limit_check(int current_minute, int64_t burst_limit, int
window_length, size_t *array, size_t incValue);
/**
* Updates rate limit arrays with passing record information
*
* @param current_minute The current minute of time the record processed
* @param window_length Window of time burst limit refers to
* @param array Pointer to array that stores record or byte count
* @param incValue Amount to increase array slot by
*
*/
void rate_limit_update(int current_minute, int window_length,
size_t *array, size_t incValue);
/**
* Checks if the spool strategy is chosen for rate-limiting
*
* @param daemon Pointer to the daemon
*
* @return true if spool is choosen false if drop is choosen
*/
bool spool_strategy_selected(TelemDaemon *daemon);
/**
* Check if daemon should direct-spool after encountering network issues.
*
* @param daemon Pointer to the daemon
* @param time The time as time_t (usually from time())
*
* @return true if inside direct spool window, false otherwise.
*/
bool inside_direct_spool_window(TelemDaemon *daemon, time_t time);
/**
* Set the start time of the direct spool window. Will use the
* current time.
*
* @param daemon Pointer to the daemon
*
* @return no return value.
*/
void start_network_bypass(TelemDaemon *daemon);
/**
* Function pointer for posting the records to the server. Replaced by a
* mock function in the unit tests.
*
* @param header_values The header data read from the socket
* @param body The payload of the record read
* @param spool Indicate whether the record should be spooled if not
* successfully delivered
*
* @return true if the record was successfully posted
*/
extern bool (*post_record_ptr)(char *headers[], char *body, bool spool);
/**
* Post records on a http connection to server
*
* @param header_values The header data read from the socket
* @param body The payload of the record read
* @param spool Indicate whether the record should be spooled if not
* successfully delivered
*
* @return true if the record was successfully posted
*/
bool post_record_http(char *header_values[], char *body, bool spool);
/**
* Spool the record in case it cannot be delivered to the server
*
* @param headers The headers for the record
* @param body The payload of the record
*
*/
void spool_record(TelemDaemon *daemon, char *headers[], char *body);
/**
* Get random machine id stored in file
*
@@ -290,4 +146,5 @@ int update_machine_id(void);
* or if the file is empty
*/
char *read_machine_id_override(void);
/* vi: set ts=8 sw=8 sts=4 et tw=80 cino=(0: */
+357 -149
View File
@@ -15,7 +15,7 @@
*/
#define _GNU_SOURCE
#include <glib.h>
#include <string.h>
#include <stdio.h>
#include <sys/stat.h>
@@ -29,10 +29,13 @@
#include <sys/un.h>
#include <limits.h>
#include <inttypes.h>
#include <ctype.h>
#include "util.h"
#include "common.h"
#include "configuration.h"
#include "telemetry.h"
#include "log.h"
/**
* Return a file descriptor to either site's version file
@@ -74,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;
@@ -137,38 +138,14 @@ static int set_severity_header(struct telem_ref *t_ref, uint32_t severity)
*/
static int set_classification_header(struct telem_ref *t_ref, char *classification)
{
size_t i, j;
int slashes = 0;
int x = 0;
j = strlen(classification);
if (j > MAX_CLASS_LENGTH) {
return -EINVAL;
}
for (i = 0, x = 0; i <= (j - 1); i++, x++) {
if (classification[i] == '/') {
slashes++;
x = 0;
} else {
if (x > MAX_SUBCAT_LENGTH) {
return -EINVAL;
}
}
}
if (slashes != 2) {
#ifdef DEBUG
fprintf(stderr, "ERR: Classification string should have two /s.\n");
#endif
if (validate_classification(classification) == 1) {
return -EINVAL;
}
return set_header(&(t_ref->record->headers[TM_CLASSIFICATION]),
TM_CLASSIFICATION_STR, classification,
&(t_ref->record->header_size));
}
/**
@@ -255,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));
}
@@ -307,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");
@@ -322,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");
}
@@ -338,7 +325,7 @@ static int set_system_build_header(struct telem_ref *t_ref)
/**
* Sets the header reporting the machine_id. Note, the machine_id is really
* managed by the telemetrics daemon (telemd), and populated by the daemon
* managed by the telemetrics daemon (telemprobd), and populated by the daemon
* as it delivers the records. Consequently, the header is simply set to a
* dummy value of 0xFFFFFFF.
*
@@ -400,6 +387,60 @@ static int set_timestamp_header(struct telem_ref *t_ref)
return status;
}
/**
* Sets cpu model for telemetry record. The information from cpu is extracted
* from /proc/cpuinfo, specifically "model name" attribute.
*
* @param t_ref Telemetry Record reference obtained from tm_create_record.
*
* @return 0 if successful, or a negative errno-style value if not.
*
*/
static int set_cpu_model_header(struct telem_ref *t_ref)
{
FILE *fs = NULL;
char buf[SMALL_LINE_BUF] = { 0 };
char *model_name = NULL;
const char *attr_name = "model name";
int status = 0;
size_t attr_len = strlen(attr_name);
size_t model_str_len = 0;
fs = fopen("/proc/cpuinfo", "r");
if (fs != NULL) {
while (fgets(buf, SMALL_LINE_BUF, fs)) {
if (strncmp(attr_name, buf, attr_len) == 0) {
model_name = strchr(buf, ':');
break;
}
}
fclose(fs);
if (model_name != NULL) {
model_str_len = strlen(model_name);
if (model_str_len > 2) {
model_name = (char *)model_name + 2 * sizeof(char);
model_name[model_str_len - 3] = '\0';
} else {
model_name = "blank";
}
} else {
model_name = "blank";
telem_log(LOG_NOTICE, "NOTICE: Unable to find attribute:%s\n", attr_name);
}
status = set_header(
&(t_ref->record->headers[TM_CPU_MODEL]),
TM_CPU_MODEL_STR, model_name,
&(t_ref->record->header_size));
} else {
telem_log(LOG_NOTICE, "NOTICE: Unable to open /proc/cpuinfo\n");
status = -1;
}
return status;
}
/**
* A healper function for set_host_type_header that reads the first line out of
* a file and chomps the newline if necessary. If the file does not exist the
@@ -427,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;
}
@@ -452,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);
@@ -471,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);
@@ -497,6 +528,118 @@ static int get_dmi_value(const char *source, const char *key, char **buf)
return ret;
}
/**
* Sets the board name for telemetry record, this record is a combination of
* board name and board vendor. This information is read from dmi filesystem
* Board Name (board_name) and Board Vendor (board_vendor).
*
* @param t_ref Telemetry Record reference obtained from tm_create_record.
*
* @return 0 if successful, or a negative errno-style value if not.
*
*/
static int set_board_name_header(struct telem_ref *t_ref)
{
int status = 0;
int rc;
char *buf = NULL;
char *bn = NULL;
char *bv = NULL;
rc = get_dmi_value("/sys/class/dmi/id/board_name", "bn", &bn);
if (rc < 0) {
status = rc;
goto cleanup;
}
rc = get_dmi_value("/sys/class/dmi/id/board_vendor", "bv", &bv);
if (rc < 0) {
status = rc;
goto cleanup;
}
rc = asprintf(&buf, "%s|%s", bn, bv);
if (rc < 0) {
status = -ENOMEM;
goto cleanup;
} else {
status = set_header(
&(t_ref->record->headers[TM_BOARD_NAME]),
TM_BOARD_NAME_STR, buf,
&(t_ref->record->header_size));
free(buf);
}
cleanup:
if (bn != NULL) {
free(bn);
}
if (bv != NULL) {
free(bv);
}
return status;
}
/**
* Sets BIOS version header for telemetry record, this information is
* read from dmi filesystem BIOS Version (bios_version).
*
* @param t_ref Telemetry Record reference obtained from tm_create_record.
*
* @return 0 if successful, or a negative errno-style value if not.
*
*/
static int set_bios_version_header(struct telem_ref *t_ref)
{
int status = 0;
int rc = 0;
char *bios_version = NULL;
rc = get_dmi_value("/sys/class/dmi/id/bios_version", "bv", &bios_version);
if (rc < 0) {
status = rc;
} else {
status = set_header(
&(t_ref->record->headers[TM_BIOS_VERSION]),
TM_BIOS_VERSION_STR, bios_version,
&(t_ref->record->header_size));
free(bios_version);
}
return status;
}
/**
* Sets the event_id header, this id is an identifier that multiple
* records can share. This means that one event can lead to multiple
* records.
*
* @param t_ref Telemetry Record reference obtained from tm_create_record.
*
* @return 0 if successful, -1 on failure
*
*/
static int set_event_id_header(struct telem_ref *t_ref)
{
int rc = 0;
char *buff = NULL;
rc = get_random_id(&buff);
if (rc == 0) {
rc = set_header(
&(t_ref->record->headers[TM_EVENT_ID]),
TM_EVENT_ID_STR, buff,
&(t_ref->record->header_size));
}
free(buff);
return rc;
}
/**
* Sets the hosttype header, which is a tuple of three values looked for
* in the dmi filesystem. System Vendor (sys_vendor), Product Name
@@ -631,9 +774,9 @@ static int set_payload_format_header(struct telem_ref *t_ref, uint32_t payload_v
return status;
}
void tm_set_config_file(char *c_file)
int tm_set_config_file(const char *c_file)
{
set_config_file(c_file);
return set_config_file(c_file);
}
/**
@@ -730,6 +873,30 @@ int allocate_header(struct telem_ref *t_ref, uint32_t severity,
goto free_and_fail;
}
i++;
if ((ret = set_board_name_header(t_ref)) < 0) {
goto free_and_fail;
}
i++;
if ((ret = set_cpu_model_header(t_ref)) < 0) {
goto free_and_fail;
}
i++;
if ((ret = set_bios_version_header(t_ref)) < 0) {
goto free_and_fail;
}
i++;
if ((ret = set_event_id_header(t_ref)) < 0) {
goto free_and_fail;
}
i++; /* Not necessary, but including for future expansion */
return ret;
@@ -746,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;
}
@@ -806,8 +961,8 @@ int payload_is_ascii(char *payload, size_t len)
int ret = 0;
for (i = 0; i < len; i++) {
if (!g_ascii_isprint((gchar)payload[i]) &&
!g_ascii_isspace((gchar)payload[i])) {
if (!isprint(payload[i]) &&
!isspace(payload[i])) {
ret = -EINVAL;
break;
}
@@ -821,45 +976,84 @@ 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;
}
/**
* Write nbytes from buf to fd. Used to send records to telemd.
* Checks for id to have length = 32 characters
* and hexadecimal characters only.
*
* @param id A pointer to string to be checked
*
* @return 0 on success, 1 on failure
*
*/
static int validate_event_id(char *id)
{
int rc = 0;
const char alphab[] = EVENT_ID_ALPHAB;
// make sure is not null
if (!id) {
return 1;
}
// 32 - 32 = 0
if ((rc = (int)(strlen(id) - EVENT_ID_LEN)) != 0) {
return 1;
}
// verify alphabet
// strspn checks for characters in alphab
if (strspn(id, alphab) != EVENT_ID_LEN) {
return 1;
}
return rc;
}
int tm_set_event_id(struct telem_ref *t_ref, char *event_id)
{
int rc = -1;
if (!validate_event_id(event_id)) {
if (t_ref && t_ref->record) {
// free default id before overriding
free(t_ref->record->headers[TM_EVENT_ID]);
// set new event_id
if (asprintf(&(t_ref->record->headers[TM_EVENT_ID]),
"%s: %s\n", TM_EVENT_ID_STR, event_id) > 0) {
rc = 0;
}
}
}
return rc;
}
/**
* Write nbytes from buf to fd. Used to send records to telemprobd.
*
* @param fd Socket fd obtained from tm_get_socket.
* @param buf Data to be written to the socket.
@@ -880,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)) {
@@ -923,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;
}
@@ -937,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;
}
@@ -996,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;
}
@@ -1006,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;
}
@@ -1019,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;
@@ -1028,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);
@@ -1089,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);
+28 -5
View File
@@ -31,7 +31,10 @@
* - a library, libtelemetry, that telemetrics probes use to create telemetrics
* records and send them to the daemon for further processing.
*
* - a daemon, telemd, that prepares the records to send to a telemetrics server
* - telemprobd, a daemon that handles communication with probes and forwards
* records to a second daemon.
*
* - telempostd, this daemon sends telemetry records to a telemetrics server
* (not included in this source tree), or spools the records on disk in case
* it's unable to successfully deliver them.
*
@@ -55,11 +58,11 @@ struct telem_ref {
/**
* Set the configuration file name to use
*
* @param c_file Absolute file name of the configuration file. If the path name
* provided is not an absolute path, or the path given cannot be successfuly
* stat'd, then the effective configuration file is left unchanged.
* @param c_file Absolute file name of the configuration file.
* @return 0 on success, or a negative errno-style value on error. In case of
* an error return the effective configuration file is left unchanged.
*/
void tm_set_config_file(char *c_file);
int tm_set_config_file(const char *c_file);
/**
* Create a new telemetrics record
@@ -85,6 +88,19 @@ void tm_set_config_file(char *c_file);
int tm_create_record(struct telem_ref **t_ref, uint32_t severity,
char *classification, uint32_t payload_version);
/**
* Sets the event_id to a user defined id
*
* @param t_ref A pointer to a telem_ref struct that was previously initialized
* this struct will have an event_id already initialized and it will be
* overriden by a user provided event_id
* @param event_id A pointer to a string that contains the 32 character length
* event_id
*
* @return 0 on success, or a negative errno-style value on error
*/
int tm_set_event_id(struct telem_ref *t_ref, char *event_id);
/**
* Set the payload field of a telemetrics record
*
@@ -104,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.
*
+10
View File
@@ -16,3 +16,13 @@ TM_3_0_0 {
tm_set_payload;
tm_send_record;
} TM_2_0_0;
TM_4_0_0 {
global:
tm_set_event_id;
} TM_3_0_0;
TM_4_1_0 {
global:
tm_is_opted_in;
} TM_4_0_0;

Some files were not shown because too many files have changed in this diff Show More