Compare commits

..
107 Commits
Author SHA1 Message Date
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
Patrick McCarty b671e770c9 Release v1.10.2
This release improves reliability of both the crash and oops probes.

* The crash probe will now reprocess a core file if it encounters
  missing symbols when creating backtraces the first time. This
  improves telemetry when run on Clear Linux OS, which downloads debuginfo
  on-the-fly as needed.

* The oops parsing will now capture "unreliable" stack frames for kernel
  oopses using the same notation as the kernel ("? "). Previously, the
  parsing code ignored the unreliable flag, so those frames appeared to be
  trustworthy frames.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2017-02-06 14:50:43 -08:00
Patrick McCarty 2409452740 crash probe: retry processing when missing symbols
Missing symbols in backtraces might indicate missing debuginfo from the
crashed program, but another possibility is that on-the-fly debuginfo
has not finished downloading. The Clear Linux OS uses an on-the-fly
debuginfo setup, so this condition is likely for the first instance of a
program crashing on the system.

To decrease likelihood of missing symbols appearing, scan the backtrace
for a frame with incomplete data after first processing, and if found,
reprocess the core file after a 10 second pause.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2017-02-06 14:28:17 -08:00
Patrick McCarty ecc60b6a3c crash probe: split core process routine out of main
For future reuse of the code for processing a core file, move it to a
new function, and call it from main().

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2017-02-06 14:28:17 -08:00
Patrick McCarty 579e7a8674 crash probe: split elfutils boilerplate out of main
For future reuse of the code to initialize elfutils for processing a
core file, move it to a new function, and call it from main().

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2017-02-06 14:28:17 -08:00
Patrick McCarty 003928f52e Preserve unreliable frames for kernel oopses
When frame addresses detected during the stack scan were not previously
found by unwinding, the string "? " is added as a prefix to the function
name.

However, the current oops parsing code strips "? " if encountered, so
the backtraces from kernel oopses are missing vital information; the
presence of "? " provides a hint for debugging a stack trace and
indicates that the frame info is "unreliable".

This commit removes the "? " strip code, ensuring the prefix is retained
by the function name, and updates unit tests that check for oops lines
that should contain the prefix.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2017-02-02 12:22:22 -08:00
Patrick McCarty c54bdab485 build: echo build configuration for configure
It's often useful to print a summary of configuration options for the
build, so add a AC_MSG_NOTICE() to the tail end of the configure script.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2017-01-20 17:35:42 -08:00
Robert Nesius 2af1d7e742 v1.10.1
Since I mistagged v1.10.0 in github.
2016-12-09 02:15:39 +00:00
Robert Nesius 278c27f9a3 v1.10.0 2016-12-09 02:09:49 +00:00
Auke Kok 3e4360e2cb Docs: Add manual pages using ronn.
This adds several new manual pages to the project, ships them
using `dist` and has a simple target to recreate the new roff
files using the `ronn` utility: `make manpages`.

These man pages are still very crude and contain the absolute
minimum amount of information. I've attempted to cross link them
as much as is reasonable.

The telemetry API is documented in `telemetry(3)`. The important
functions themselves are `linked` with a ref link that should
make `man tm_create_record` (etc.) link to telemetry(3).

I attempted to make this pass `make distcheck` and `make install`
properly, but this should still be checked.
2016-12-08 17:47:25 -08:00
John Andersen cb622a0eb0 Validation for classification to ensure 2 slashes (#4)
* Validation for classification to ensure 2 slashes

Signed-off-by: John Andersen <john.s.andersen@intel.com>

* Update telem_record_gen.c

Fixed code style issues
2016-09-02 00:43:48 -07:00
Jeremy Rocher 44e0d4d3c5 crashprobe: fix stderr when launched from kernel
stderr could be used for logging or in libtelemetry in DEBUG mode but
when crashprobe is launched through kernel core_pattern, stderr is not
open, only stdin (fd 0), see this dump from gdb:

 (gdb) shell ls -l /proc/15478/fd
 total 0
 lr-x------ 1 root root 64 24 juin  15:02 0 -> pipe:[82555]

If stderr is not open, when socket to telemd is open it could take
fd 2 and in this case all fprint to stderr are written to socket which
is bad ... this is the case when doing static linking, which ends to
telemd crashed.

Fix it by ensuring stderr is open.

Signed-off-by: Jeremy Rocher <jeremy.rocher@intel.com>
2016-08-17 21:09:33 +00:00
Loic Poulain 36f4854967 Make daemon recycling configurable
The user may want to disable recycling (daemon auto exit).
This can be the case if telemd daemon is not configured
to respawn or if service is automatically restarted by
the system after package update.

This patch introduces the daemon_recycling_enabled config
entry.
2016-08-17 21:08:24 +00:00
Jeremy Rocher 524d5d2345 fix telem_perror when logging to stderr
When logging to stderr, telem_perror output is incomplete and badly
formatted. Print errno correctly, fix newline and fix includes.

Signed-off-by: Jeremy Rocher <jeremy.rocher@intel.com>
2016-08-17 13:42:39 -07:00
58 changed files with 3916 additions and 649 deletions
+35
View File
@@ -0,0 +1,35 @@
dist: trusty
compiler:
- gcc
os:
- linux
before_script:
./autogen.sh
language: c
addons:
apt:
sources:
- ubuntu-toolchain-r-test
packages:
- libcurl4-gnutls-dev
- valgrind
- autoconf
- automake
- check
- libelf-dev
- libdw-dev
install:
- wget http://downloads.sourceforge.net/project/check/check/0.10.0/check-0.10.0.tar.gz
- tar -xvf check-0.10.0.tar.gz
- pushd check-0.10.0 && ./configure --prefix=/usr --libdir=/usr/lib/x86_64-linux-gnu && make -j48 && sudo make install && popd
script:
- ./configure && make && make check
after_failure: cat test-suite.log
+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:
+2
View File
@@ -14,9 +14,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)/tests/local.mk
-38
View File
@@ -1,38 +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.
Generating HTML documentation
=============================
At toplevel, run 'doxygen docs/Doxyfile'. The documentation is generated in
the docs/html/ directory.
+192
View File
@@ -0,0 +1,192 @@
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
- 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.
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 daemon
---------------------
Method 1 (recommended):
```{r, engine='bash', count_lines}
systemctl start telemd.socket telemd.path
```
Note: the above invocation technically readies the service for both socket and
path activation, so you may not see the daemon start right away.
Method 2:
```{r, engine='bash', count_lines}
systemctl start telemd.service
```
Method 3:
```{r, engine='bash', count_lines}
telemd &
```
Configure the daemon to autostart at boot
---------------------
Method 1 (recommended):
Enable the socket-activated service and path unit:
```{r, engine='bash', count_lines}
systemctl enable telemd.socket telemd.path
```
Method 2:
Enable the service itself, which automatically enables the socket and path
units as well:
```{r, engine='bash', count_lines}
systemctl enable telemd.service
```
Usage
---------------------
Once the daemon 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 daemon uses the following configuration options from the configuration file:
* server: This specifies the web server that the daemon sends the telemetry records to
* socket_path: This specifies the path of the unix domain socket that the
daemon 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 telemd.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.
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 daemon 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 telemd for the machine id
changes to take effect by running:
```{r, engine='bash', count_lines}
# systemctl restart telemd.service
```
You can switch back to the rotating machine id by deleting the override file
and restarting the daemon. You can do a quick test to check that your
machine-id has changed by running "hprobe" and verifying that a record has
landed on your backend telemetrics server, with the specified machine id.
-2
View File
@@ -2,7 +2,6 @@ AM_CFLAGS = \
-std=gnu99 \
-pedantic \
-Wall \
-fstack-protector \
-Wformat \
-Wformat-security \
-Wimplicit-function-declaration \
@@ -12,7 +11,6 @@ AM_CFLAGS = \
-Wconversion \
-Wunreachable-code \
-funsigned-char \
-fstack-protector-strong \
-fPIE \
-fPIC
+27 -2
View File
@@ -2,7 +2,7 @@
# Process this file with autoconf to produce a configure script.
AC_PREREQ([2.69])
AC_INIT([telemetrics-client], [1.9.0], [https://clearlinux.org/])
AC_INIT([telemetrics-client], [1.13.0], [https://clearlinux.org/])
AC_CONFIG_AUX_DIR([build-aux])
AM_INIT_AUTOMAKE([1.14 -Wall -Werror -Wno-extra-portability foreign subdir-objects])
AM_SILENT_RULES([yes])
@@ -21,9 +21,10 @@ AC_PROG_LN_S
AC_PROG_MAKE_SET
# Checks for libraries.
# check >= 0.9.12 is required for TAP output
PKG_CHECK_MODULES([CHECK], [check >= 0.9.12])
PKG_CHECK_MODULES([CURL], [libcurl])
PKG_CHECK_MODULES([GLIB], [glib-2.0])
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"],
@@ -137,3 +138,27 @@ AC_CONFIG_COMMANDS([config dir],[mkdir -p src/data; touch src/data/.dirstamp])
AC_CONFIG_FILES([Makefile])
AC_REQUIRE_AUX_FILE([tap-driver.sh])
AC_OUTPUT
AC_MSG_NOTICE([
telemetrics-client $VERSION
===========================
prefix: $prefix
exec_prefix: $exec_prefix
libdir: $libdir
bindir: $bindir
datarootdir: $datarootdir
compiler: $CC
cflags: $CFLAGS
ldflags: $LDFLAGS
unitdir: $unitpath
tmpfilesdir: $tmpfilespath
sysctldir: $sysctlpath
systemconfdir: $confpath
socketdir: $socketpath
loglevel: $loglevel
logtype: $logtype
])
+13
View File
@@ -0,0 +1,13 @@
## 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`
files and not the shipped nroff output files.
## Generating HTML documentation
At toplevel, run 'doxygen docs/Doxyfile'. The documentation is
generated in the docs/html/ directory.
+23
View File
@@ -0,0 +1,23 @@
MANPAGES = \
docs/man/telemctl.1 \
docs/man/telemd.1 \
docs/man/telem-record-gen.1 \
docs/man/telemetry.3 \
docs/man/telemetrics.conf.5
MANLINKS = \
docs/man/tm_create_record.3 \
docs/man/tm_send_record.3 \
docs/man/tm_set_config_file.3 \
docs/man/tm_set_payload.3
manpages:
for MANPAGE in $(MANPAGES); do \
rst2man.py $${MANPAGE}.rst > $${MANPAGE}; \
rst2html.py $${MANPAGE}.rst > $${MANPAGE}.html; \
done
dist_man_MANS = \
$(MANPAGES) $(MANLINKS)
# vim: filetype=automake tabstop=8 shiftwidth=8 noexpandtab
+1
View File
@@ -0,0 +1 @@
*.html
+98
View File
@@ -0,0 +1,98 @@
.\" Man page generated from reStructuredText.
.
.TH TELEM-RECORD-GEN 1 "" "" ""
.SH NAME
telem-record-gen \- Create custom telemetry record
.
.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
\fBtelem\-record\-gen\fP <options>
.SH DESCRIPTION
.sp
Generate and attempt to send a telemetry record to \fBtelemd\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 \fBtelemd\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).
.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
\fBtelemd\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.
.
+78
View File
@@ -0,0 +1,78 @@
================
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 ``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.
SEE ALSO
========
* ``telemd``\(1)
* https://github.com/clearlinux/telemetrics-client
* https://clearlinux.org/documentation/
+76
View File
@@ -0,0 +1,76 @@
.\" Man page generated from reStructuredText.
.
.TH TELEMCTL 1 "" "" ""
.SH NAME
telemctl \- Telemetry service administration tool
.
.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
\fBtelemctl\fP
.sp
\fB/etc/telemetrics/opt\-out\fP
.SH DESCRIPTION
.sp
Control actions for telemetry services. The command can be used to start,
restart, or stop \fBtelemd\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 starts telemetry services. The opt\-out file
\fB/etc/telemetrics/opt\-out\fP is removed.
.IP \(bu 2
\fBopt\-out\fP:
Opts out of telemetry, and stops telemetry services. The opt\-out file
\fB/etc/telemetrics/opt\-out\fP is created.
.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
\fBtelemd\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.
.
+56
View File
@@ -0,0 +1,56 @@
========
telemctl
========
-------------------------------------
Telemetry service administration tool
-------------------------------------
:Copyright: \(C) 2017 Intel Corporation, CC-BY-SA-3.0
:Manual section: 1
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.
SEE ALSO
========
* ``telemd``\(1)
* https://github.com/clearlinux/telemetrics-client
* https://clearlinux.org/documentation/
+106
View File
@@ -0,0 +1,106 @@
.\" Man page generated from reStructuredText.
.
.TH TELEMD 1 "" "" ""
.SH NAME
telemd \- 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
\fBtelemd\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 \fBtelemd\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 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, \fBtelemd\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 \fBtelemd\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 @@
======
telemd
======
------------------------
Telemetry client service
------------------------
:Copyright: \(C) 2017 Intel Corporation, CC-BY-SA-3.0
:Manual section: 1
SYNOPSIS
========
``telemd`` \<flags\>
``/etc/telemetrics/telemetrics.conf``
``/usr/share/defaults/telemetrics/telemetrics.conf``
``/etc/telemetrics/opt-in-static-machine-id``
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.
FILES
=====
* ``/usr/share/defaults/telemetrics/telemetrics.conf``
If no custom configuration file is found, ``telemd`` uses the
settings in this file.
* ``/etc/telemetrics/telemetrics.conf``
Custom configuration file that ``telemd`` 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/
+126
View File
@@ -0,0 +1,126 @@
.\" Man page generated from reStructuredText.
.
.TH TELEMETRICS.CONF 5 "" "" ""
.SH NAME
telemetrics.conf \- Telemetry service configuration file
.
.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
\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 \fBtelemd\fP(1) telemetry
service daemon. 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 \fItelemd\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..3600. 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
\fBtelemd\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.
.
+110
View File
@@ -0,0 +1,110 @@
================
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 ``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``.
SEE ALSO
========
* ``telemd``\(1)
* https://github.com/clearlinux/telemetrics-client
* https://clearlinux.org/documentation/
+81 -12
View File
@@ -1,15 +1,84 @@
.TH TELEMETRY 3 "April 2015" Telemetrics
.\" Man page generated from reStructuredText.
.
.TH TELEMETRY 3 "" "" ""
.SH NAME
telemetry \- Telemetry record creation and sending
telemetry \- C programming interface for telemetrics-client
.
.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
#include <telemetry.h>
.sp
tm_create_record()
.br
tm_set_payload()
.br
tm_send_record()
.br
tm_set_config_File()
\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
\fBvoid tm_set_config_file(char *c_file)\fP
.SH DESCRIPTION
.sp
The functions in the telemetry library facilitate the delivery of
telemetry data to the \fBtelemd\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
\fBtelemd\fP(1) service.
.sp
The function \fBtm_set_config_file()\fP can be used to provide an alternate
configuration path to the telemetry library.
.SH RETURN VALUES
.sp
All these functions return \fB0\fP on success, or a non\-zero return value
if an error occurred. The functions \fBtm_free_record()\fP and \fBtm_set_config_file()\fP
do not return any values.
.SH SEE ALSO
.INDENT 0.0
.IP \(bu 2
\fBtelemd\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.
.
+67
View File
@@ -0,0 +1,67 @@
=========
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)``
``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.
SEE ALSO
========
* ``telemd``\(1)
* https://github.com/clearlinux/telemetrics-client
* https://clearlinux.org/documentation/
+1
View File
@@ -0,0 +1 @@
.so man3/telemetry.3
+1
View File
@@ -0,0 +1 @@
.so man3/telemetry.3
+1
View File
@@ -0,0 +1 @@
.so man3/telemetry.3
+1
View File
@@ -0,0 +1 @@
.so man3/telemetry.3
+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
+47 -24
View File
@@ -16,22 +16,25 @@
#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 "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 */
@@ -44,7 +47,8 @@ const char *config_key_int[] = { NULL, "record_expiry", "spool_max_size",
"byte_window_length", "record_burst_limit",
"byte_burst_limit", NULL };
const char *config_key_bool[] = { NULL, "rate_limit_enabled", NULL };
const char *config_key_bool[] = { NULL, "rate_limit_enabled",
"daemon_recycling_enabled", NULL };
static struct configuration config = { { 0 }, { 0 }, { 0 }, false, NULL };
@@ -80,16 +84,12 @@ void set_config_file(char *filename)
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)) {
keyfile = nc_ini_file_parse(config_file);
if (!keyfile) {
#ifdef DEBUG
fprintf(stderr, "ERR: Failed to read config file: %s\n",
error->message);
@@ -97,24 +97,43 @@ bool read_config_from_file(char *config_file, struct configuration *config)
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) {
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) {
return false;
}
} else {
fprintf(stderr, "ERR: missing key with string value: %s\n", config_key_str[i]);
return false;
}
}
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) {
char *ptr;
ptr = nc_hashmap_get(nc_hashmap_get(keyfile, "settings"), config_key_int[i]);
if (ptr) {
config->intValues[i] = strtoll(ptr, NULL, 10);
} else {
fprintf(stderr, "ERR: missing key with integer value: %s\n", config_key_int[i]);
return false;
}
}
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) {
char *ptr;
ptr = nc_hashmap_get(nc_hashmap_get(keyfile, "settings"), config_key_bool[i]);
config->boolValues[i] = false;
if (ptr) {
if (strcasecmp(ptr, "TRUE") == 0) {
config->boolValues[i] = true;
}
if (strcasecmp(ptr, "1") == 0) {
config->boolValues[i] = true;
}
} else {
fprintf(stderr, "ERR: missing key with boolean value: %s\n", config_key_bool[i]);
return false;
}
}
@@ -132,11 +151,11 @@ 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 */
@@ -178,8 +197,6 @@ void free_configuration(void)
free(config.strValues[i]);
}
g_key_file_free(keyfile);
if (cmd_line_cfg) {
free(config_file);
}
@@ -334,4 +351,10 @@ const char *rate_limit_strategy_config()
return val;
}
bool daemon_recycling_enabled_config(void)
{
initialize_config();
return config.boolValues[CONF_DAEMON_RECYCLING_ENABLED];
}
/* vi: set ts=8 sw=8 sts=4 et tw=80 cino=(0: */
+4
View File
@@ -46,6 +46,7 @@ enum config_int_keys {
enum config_bool_keys {
CONF_BOOL_MIN = 0,
CONF_RATE_LIMIT_ENABLED,
CONF_DAEMON_RECYCLING_ENABLED,
CONF_BOOL_MAX
};
@@ -113,4 +114,7 @@ const char *get_cainfo_config(void);
/* Gets tidheader */
const char *get_tidheader_config(void);
/* Gets whether recycling is enabled */
bool daemon_recycling_enabled_config(void);
/* vi: set ts=8 sw=8 sts=4 et tw=80 cino=(0: */
+3
View File
@@ -35,3 +35,6 @@ rate_limit_enabled=true
#rate limit strategy if record not send
rate_limit_strategy=spool
#daemon recycling enabled
daemon_recycling_enabled=true
+4
View File
@@ -51,3 +51,7 @@ byte_window_length=20
# Valid stategies: spool, drop
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
+4 -8
View File
@@ -44,20 +44,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 +67,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 -3
View File
@@ -20,6 +20,7 @@
#include "config.h"
#ifdef TM_LOG_SYSTEMD
#ifdef HAVE_SYSTEMD_SD_JOURNAL_H
/* Certain static analysis tools do not understand GCC's __INCLUDE_LEVEL__
@@ -32,6 +33,7 @@
#endif
#include <systemd/sd-journal.h>
#endif
#else
#include <errno.h>
#include <string.h>
@@ -79,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
@@ -88,10 +90,10 @@
#define telem_perror(msg) sd_journal_perror(msg)
#endif
#ifdef TM_LOG_SYSLOG
#define telem_perror(msg) syslog(LOG_ERR, msg ": %s", strerror(errno))
#define telem_perror(msg) syslog(LOG_ERR, msg ": %s\n", strerror(errno))
#endif
#ifdef TM_LOG_STDERR
#define telem_perror(msg) fprintf(stderr, ": %s", msg)
#define telem_perror(msg) fprintf(stderr, "ERROR: " msg ": %s\n", strerror(errno))
#endif
/* vi: set ts=8 sw=8 sts=4 et tw=80 cino=(0: */
+3 -1
View File
@@ -232,6 +232,8 @@ int main(int argc, char **argv)
time_t last_spool_run_time = time(NULL);
time_t last_daemon_start_time = time(NULL);
bool daemon_recycling_enabled = daemon_recycling_enabled_config();
ret = update_machine_id();
if (ret == -1) {
telem_log(LOG_ERR, "Unable to update machine id\n");
@@ -336,7 +338,7 @@ int main(int argc, char **argv)
} else {
time_t now = time(NULL);
/* If spool is empty and time to recycle the daemon has elapsed*/
if ((daemon.current_spool_size == 0) &&
if (daemon_recycling_enabled && (daemon.current_spool_size == 0) &&
difftime(now, last_daemon_start_time) >= TM_DAEMON_EXIT_TIME) {
/* Exit */
telem_log(LOG_INFO, "Daemon exiting for recycling\n");
+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:
*/
+386
View File
@@ -0,0 +1,386 @@
/*
* 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);
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);
value = string_unescape(value);
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:
*/
+331 -166
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,13 +32,13 @@
#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"
static Dwfl *d_core = NULL;
@@ -53,10 +54,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 +68,10 @@ 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 bool keep_core = false;
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
@@ -176,6 +180,55 @@ static int temp_core_file(void)
return tmp;
}
/* There are a number of initialization routines required to initialize the Elf
* and Dwfl objects used by libdwfl to later process a core file. The argument
* e_core is the address of an Elf pointer declared by the caller, and core_fd
* is the open file descriptor for the core file.
*/
static int prepare_corefile(Elf **e_core, int core_fd)
{
// Cleanup previous corefile processing if needed
if (d_core) {
dwfl_end(d_core);
}
if (*e_core) {
elf_end(*e_core);
}
// Boilerplate initialization for elfutils (libdwfl)
if (!(*e_core = elf_begin(core_fd, ELF_C_READ, NULL))) {
tm_elf_err("Failed to get file descriptor for ELF core file");
goto fail;
}
if (!(d_core = dwfl_begin(&cb))) {
tm_dwfl_err("Failed to start new libdwfl session");
goto fail;
}
int core_modules = 0;
core_modules = dwfl_core_file_report(d_core, *e_core, NULL);
if (core_modules == -1) {
tm_dwfl_err("Failed to report modules for ELF core file");
goto fail;
}
if (dwfl_report_end(d_core, NULL, NULL) != 0) {
tm_dwfl_err("Failed to finish reporting modules");
goto fail;
}
if ((core_for_pid = dwfl_core_file_attach(d_core, *e_core)) < 0) {
tm_dwfl_err("Failed to prepare libdwfl session for thread"
" iteration");
goto fail;
}
return 0;
fail:
return -1;
}
/* This callback is invoked for every frame in a thread. From a Dwfl_Frame, we
* are able to extract the program counter (PC), and from that, the procedure
* name via a Dwfl_Module.
@@ -185,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) {
return DWARF_CB_ABORT;
}
return DWARF_CB_ABORT;
}
@@ -210,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;
}
@@ -219,41 +277,54 @@ 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");
return DWARF_CB_OK;
}
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 */
@@ -269,14 +340,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;
@@ -305,80 +376,145 @@ fail:
return false;
}
static bool in_clr_build(gchar *fullpath)
/* This is the entry point for libdwfl to unwind the backtrace from the core
* file. All data necessary for processing is referenced by d_core (a *Dwfl),
* 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
* nc_string object declared by the caller which stores the backtrace data as a
* string.
*/
static int process_corefile(nc_string **backtrace)
{
if (*backtrace) {
nc_string_free(*backtrace);
}
*backtrace = nc_string_dup("");
if (dwfl_getthreads(d_core, thread_cb, backtrace) != DWARF_CB_OK) {
/* 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
* some frames were processed.
*/
if (errorstr != NULL) {
telem_log(LOG_ERR, "%s", errorstr);
nc_string_append_printf(header, "Error: %s", errorstr);
nc_string_prepend(*backtrace, header->str);
send_data(backtrace, error_severity, error_class);
}
goto fail;
}
return 0;
fail:
return -1;
}
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 *config_file = NULL;
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 Path to configuration file (not implemented yet)\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)
{
Elf *e_core = NULL;
int core_modules = 0;
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
// fd 2 when launched from kernel core pattern
if (freopen("/dev/null", "w", stderr) == NULL) {
exit(EXIT_FAILURE);
}
}
// Since this program handles core files, make sure it does not produce
// core files itself, or else it will be invoked endlessly...
@@ -386,46 +522,54 @@ 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':
config_file = strdup(optarg);
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) {
@@ -452,62 +596,74 @@ 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");
keep_core = true;
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");
keep_core = true;
if (!send_data(&backtrace, unknown_severity, unknown_class)) {
goto fail;
}
goto success;
}
elf_version(EV_CURRENT);
if (!(e_core = elf_begin(core_fd, ELF_C_READ, NULL))) {
tm_elf_err("Failed to get file descriptor for ELF core file");
if (prepare_corefile(&e_core, core_fd) < 0) {
goto fail;
}
if (!(d_core = dwfl_begin(&cb))) {
tm_dwfl_err("Failed to start new libdwfl session");
goto fail;
}
core_modules = dwfl_core_file_report(d_core, e_core, NULL);
if (core_modules == -1) {
tm_dwfl_err("Failed to report modules for ELF core file");
goto fail;
}
if (dwfl_report_end(d_core, NULL, NULL) != 0) {
tm_dwfl_err("Failed to finish reporting modules");
goto fail;
}
if ((core_for_pid = dwfl_core_file_attach(d_core, e_core)) < 0) {
tm_dwfl_err("Failed to prepare libdwfl session for thread"
" iteration");
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);
nc_string_append_printf(header, "Signal: %ld\n", signal_num);
}
backtrace = g_string_new(NULL);
if (dwfl_getthreads(d_core, thread_cb, &backtrace) != DWARF_CB_OK) {
if (errorstr != NULL) {
telem_log(LOG_ERR, "%s", errorstr);
g_string_append_printf(header, "Error: %s", errorstr);
g_string_prepend(backtrace, header->str);
send_data(&backtrace, error_severity, error_class);
}
if (process_corefile(&backtrace) < 0) {
goto fail;
}
g_string_prepend(backtrace, header->str);
/* 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.
*/
if (strstr(backtrace->str, "??? - [")) {
sleep(10);
if (prepare_corefile(&e_core, core_fd) < 0) {
goto fail;
}
if (process_corefile(&backtrace) < 0) {
goto fail;
}
}
nc_string_prepend(backtrace, header->str);
if (!send_data(&backtrace, default_severity, clr_class)) {
goto fail;
@@ -522,22 +678,25 @@ success:
ret = EXIT_SUCCESS;
fail:
free_glib_strings();
if (context) {
g_option_context_free(context);
// Do not remove the core file if any errors occur
if (ret == EXIT_FAILURE) {
keep_core = true;
}
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) {
@@ -552,6 +711,12 @@ fail:
close(core_fd);
}
// Remove the core file by default, except when the --core-file option
// is specified, or when keep_core is overridden to true.
if (!core_file && !keep_core) {
unlink(temp_core);
}
return ret;
}
+123 -72
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,24 +225,42 @@ 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;
}
@@ -258,50 +307,56 @@ 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 char *config_file = NULL;
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' },
{ 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 Path to configuration file (not implemented yet)\n");
printf(" -V, --version Print the program version\n");
printf("\n");
}
static void free_strings(void)
{
free(config_file);
}
int main(int argc, char **argv)
{
int ret = EXIT_FAILURE;
GError *error = NULL;
GOptionContext *context;
int opt;
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;
}
if (version_p) {
g_print(PACKAGE_VERSION "\n");
goto success;
while ((opt = getopt_long(argc, argv, "hf:V", prog_opts, NULL)) != -1) {
switch (opt) {
case 'h':
print_help();
goto success;
case 'V':
printf(PACKAGE_VERSION "\n");
goto success;
case 'f':
config_file = strdup(optarg);
break;
}
}
if (!process_journal()) {
@@ -311,18 +366,14 @@ int main(int argc, char **argv)
success:
ret = EXIT_SUCCESS;
fail:
free_glib_strings();
free_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;
+5 -4
View File
@@ -7,6 +7,7 @@
#include <errno.h>
#include <unistd.h>
#include <malloc.h>
#include <signal.h>
#include "log.h"
#include "oops_parser.h"
@@ -22,7 +23,6 @@ int main(void)
char *bufp = NULL;
size_t buflen = 0;
int bytes = 0;
int loopcount = 0;
oops_parser_init(write_oops_to_file);
@@ -44,20 +44,21 @@ int main(void)
// Gets the size of the kernel ring buffer
log_size = klogctl(SYSLOG_ACTION_SIZE_BUFFER, NULL, 0);
if (log_size < 0) {
telem_log(LOG_ERR, "Cannot read size of kernel ring buffer\n");
telem_log(LOG_ERR, "Cannot read size of kernel ring buffer: %s\n", strerror(errno));
return 1;
}
buflen = (size_t)log_size;
if (loopcount++ > 0 && buflen > MAX_BUF)
if (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);
bytes = klogctl(SYSLOG_ACTION_READ, bufp, (int)buflen);
if (bytes < 0) {
telem_log(LOG_ERR, "Cannot read contents of kernel ring buffer: %s\n", strerror(errno));
return 1;
-1
View File
@@ -22,7 +22,6 @@
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <glib.h>
#include <sys/stat.h>
#include "log.h"
+19 -24
View File
@@ -15,10 +15,8 @@ bin_PROGRAMS += \
%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
@@ -44,15 +42,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 +66,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,13 +88,12 @@ 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
@@ -120,14 +116,14 @@ endif
%C%_oopsprobe_SOURCES = \
%D%/oops_probe.c \
%D%/oops_parser.c
%D%/oops_parser.c \
src/nica/nc-string.c \
%D%/probe.h
%C%_oopsprobe_CFLAGS = \
$(AM_CFLAGS) \
$(GLIB_CFLAGS)
$(AM_CFLAGS)
%C%_oopsprobe_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
@@ -144,16 +140,15 @@ 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 = \
+99 -55
View File
@@ -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
@@ -182,7 +184,7 @@ char *skip_timestamp(char *line)
return start;
}
char *skip_spaces(char *line)
char *skip_space(char *line)
{
char *start = line;
@@ -192,6 +194,16 @@ char *skip_spaces(char *line)
return start;
}
char *skip_spaces(char *line)
{
char *start = line;
while (*start && isspace(*start)) {
start++;
}
return start;
}
bool starts_with(const char *line, const char *line_end, const char *substr)
{
size_t substrlen = strlen(substr);
@@ -344,7 +356,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) {
@@ -376,13 +388,16 @@ void parse_single_line(char *line, size_t size)
} 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:")) {
// 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)) {
@@ -425,9 +440,20 @@ struct stack_frame {
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 +462,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') {
if (start && *start == '\0') {
return;
}
start++;
// Skip intervening space
start = skip_spaces(start);
}
frame = malloc(sizeof(struct stack_frame));
@@ -462,26 +492,29 @@ 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);
} else {
frame->addr = 0;
}
/* Skip up to the space after the closing bracket */
while (*start && !isspace(*start)) {
start++;
}
if (*start) {
start++;
}
if (*start == '<') {
start++;
frame->addr = (uint64_t)strtoll(start, (char **)&start, 16);
} else {
frame->addr = 0;
}
if (*start && *start == '?') {
start++;
if (*start && isspace(*start)) {
// Skip up to the space after the closing bracket
while (*start && !isspace(*start)) {
start++;
}
if (start && *start == '\0') {
return;
}
// Skip intervening space
start = skip_spaces(start);
} else {
frame->addr = 0;
}
offset_ptr = strchr(start, '+');
@@ -706,18 +739,19 @@ void parse_registers(char *line)
}
void append_registers_to_bt(GString **backtrace)
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;
}
@@ -728,25 +762,36 @@ void append_registers_to_bt(GString **backtrace)
}
}
GString *parse_backtrace(struct oops_log_msg *msg)
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;
@@ -760,23 +805,23 @@ GString *parse_backtrace(struct oops_log_msg *msg)
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) {
@@ -784,24 +829,24 @@ 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)
nc_string *parse_payload(struct oops_log_msg *msg)
{
GString *payload, *backtrace;
nc_string *payload, *backtrace;
payload = g_string_new("Crash Report:\n");
g_string_append_printf(payload, "Reason: %s\n", msg->lines[0]);
payload = nc_string_dup("Crash Report:\n");
nc_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);
nc_string_cat(payload, backtrace->str);
nc_string_free(backtrace);
return payload;
}
@@ -814,4 +859,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: */
+4 -4
View File
@@ -17,11 +17,9 @@
#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>
@@ -29,6 +27,8 @@
#include "log.h"
#include "telemetry.h"
#include "nica/nc-string.h"
char *oops_dir_path = KERNELOOPSDIR;
static uint32_t version = 1;
@@ -68,7 +68,7 @@ void handle_oops_file(const char *fname)
char *filename = NULL, *contents = NULL;
long sz;
size_t size, bytes_read;
GString *payload;
nc_string *payload;
ret = asprintf(&filename, "%s/%s", oops_dir_path, fname);
if (ret == -1) {
@@ -138,7 +138,7 @@ void handle_oops_file(const char *fname)
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);
nc_string_free(payload);
} else {
/* File does no contain an oops! */
telem_log(LOG_ERR, "Did not find an oops in the oops directort\n");
+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: */
+18 -18
View File
@@ -12,11 +12,10 @@
* A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*/
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <glib.h>
#include <string.h>
#include <errno.h>
#include <assert.h>
@@ -26,6 +25,7 @@
#include "log.h"
#include "telemetry.h"
#include "oops_parser.h"
#include "nica/hashmap.h"
char *pstore_dump_path = PSTOREDIR;
@@ -61,7 +61,7 @@ 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++) {
@@ -74,7 +74,7 @@ void handle_complete_oops_message(struct oops_log_msg *msg)
printf("Payload Parsed :%s\n", payload->str);
#endif
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 +100,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 +108,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 +173,7 @@ end:
fclose(fp);
}
g_free(dump_file);
free(dump_file);
return contents;
}
@@ -199,8 +202,8 @@ 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;
@@ -209,7 +212,7 @@ int main(int argc, char **argv)
* 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) {
@@ -247,23 +250,20 @@ 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)) {
nc_hashmap_iter_init(hash, &iter);
while (nc_hashmap_iter_next(&iter, (void **)&key, (void **)&value)) {
#ifdef DEBUG
printf("Count in the hash: %d\n", *((int *)key));
printf("Count in the hash: %d\n", NC_UNHASH_KEY(key));
#endif
head = (struct chunk_list *)value;
+118 -64
View File
@@ -15,6 +15,8 @@
*/
#define _GNU_SOURCE
#include <ctype.h>
#include <getopt.h>
#include <poll.h>
#include <stdbool.h>
#include <stdio.h>
@@ -22,84 +24,133 @@
#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 char *config_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 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' },
{ "config-file", required_argument, 0, 'f' },
{ "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' },
{ 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(" 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(" -f, --config-file Path to configuration file (not implemented yet)\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\n");
printf(" -p, --payload Record body (max size = 8k)\n");
printf(" -P, --payload-file File to read payload from\n");
printf(" -R, --record-version Version number for format of payload (default 1)\n");
printf("\n");
}
if (opt_class) {
g_free(opt_class);
const unsigned int count_chars(const char *check, const char character)
{
unsigned int count = 0U;
if (check == NULL) {
return count;
}
if (opt_payload) {
g_free(opt_payload);
for (; *check != '\0'; ++check) {
if (*check == character) {
++count;
}
}
return count;
}
int parse_options(int argc, char **argv)
{
int ret = 0;
GError *error = NULL;
GOptionContext *context;
char *endptr = NULL;
long unsigned int tmp = 0;
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);
int opt;
while ((opt = getopt_long(argc, argv, "hc:Vs:c:p:P:R:", 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;
if (!g_option_context_parse(context, &argc, &argv, &error)) {
g_print("Failed to parse options: %s\n", error->message);
goto fail;
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':
opt_class = strdup(optarg);
break;
case 'p':
opt_payload = strdup(optarg);
break;
case 'P':
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;
}
}
ret = 1;
fail:
if (context) {
g_option_context_free(context);
}
return ret;
}
int validate_opts(void)
{
size_t i;
size_t len;
int ret = 0;
/* classification */
@@ -108,17 +159,25 @@ int validate_opts(void)
return ret;
}
i = strlen(opt_class);
len = strlen(opt_class);
if ((i == 0) || (i > 120)) {
if ((len == 0) || (len > 120)) {
fprintf(stderr, "Error: Valid size for classification "
"is 1-120 chars\n");
return ret;
}
if (!g_str_is_ascii(opt_class)) {
fprintf(stderr, "Error: Non-ascii characters detected "
"in classification - aborting\n");
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) {
fprintf(stderr, "Error: Classification needs to be in "
"most/to/least specific format, 2 \'/\' required.\n");
return ret;
}
@@ -135,7 +194,7 @@ int allocate_payload_buffer(char **payload)
{
int ret = 0;
*payload = (char *)malloc(sizeof(gchar) * MAX_PAYLOAD_SIZE);
*payload = (char *)malloc(MAX_PAYLOAD_SIZE);
if (*payload == NULL) {
goto out1;
@@ -162,7 +221,7 @@ int get_payload_from_file(char **payload)
goto out;
}
bytes_in = fread(*payload, sizeof(gchar),
bytes_in = fread(*payload, 1,
MAX_PAYLOAD_SIZE - 1, fp);
/* if fread fails */
@@ -195,7 +254,7 @@ 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_SIZE - 1, stdin);
if (bytes_in == MAX_PAYLOAD_SIZE - 1) {
/* Throw away the rest of stdin */
@@ -272,11 +331,6 @@ int main(int argc, char **argv)
goto fail;
}
if (version_p) {
g_print(PACKAGE_VERSION "\n");
goto success;
}
if (!validate_opts()) {
goto fail;
}
@@ -289,13 +343,13 @@ int main(int argc, char **argv)
goto fail;
}
success:
ret = EXIT_SUCCESS;
fail:
free_glib_strings();
free(config_file);
free(opt_class);
free(opt_payload);
return ret;
}
/* vi: set ts=8 sw=8 sts=4 et tw=80 cino=(0: */
+12 -4
View File
@@ -462,6 +462,11 @@ bool post_record_http(char *headers[], char *body, bool spool)
const char *cert_file = get_cainfo_config();
const char *tid_header = get_tidheader_config();
// Initialize the libcurl global environment once per POST. This lets us
// clean up the environment after each POST so that when the daemon is
// sitting idle, it will be consuming as little memory as possible.
curl_global_init(CURL_GLOBAL_ALL);
curl = curl_easy_init();
if (!curl) {
telem_log(LOG_ERR, "curl_easy_init(): Unable to start libcurl"
@@ -511,11 +516,11 @@ bool post_record_http(char *headers[], char *body, bool spool)
if (res) {
size_t len = strlen(errorbuf);
if (len) {
telem_log(LOG_ERR, "Failed sending record: %s%s", errorbuf,
((errorbuf[len - 1] != '\n') ? "\n" : ""));
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));
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
@@ -532,6 +537,9 @@ bool post_record_http(char *headers[], char *body, bool spool)
curl_slist_free_all(custom_headers);
curl_easy_cleanup(curl);
curl_global_cleanup();
return res ? false : true;
}
+3 -3
View File
@@ -15,7 +15,6 @@
*/
#define _GNU_SOURCE
#include <glib.h>
#include <string.h>
#include <stdio.h>
#include <sys/stat.h>
@@ -29,6 +28,7 @@
#include <sys/un.h>
#include <limits.h>
#include <inttypes.h>
#include <ctype.h>
#include "common.h"
#include "configuration.h"
@@ -806,8 +806,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;
}
+2
View File
@@ -50,6 +50,7 @@ START_TEST(check_read_valid_config)
ck_assert_str_eq(config.strValues[CONF_CAINFO], "/tmp/cacert.crt");
ck_assert_str_eq(config.strValues[CONF_TIDHEADER],
"X-Telemetry-TID: 6907c830-eed9-4ce9-81ae-76daf8d88f0f");
ck_assert(config.boolValues[CONF_DAEMON_RECYCLING_ENABLED] == true);
}
END_TEST
@@ -75,6 +76,7 @@ START_TEST(check_config_initialised)
ck_assert_str_eq(get_cainfo_config(), "/tmp/cacert.crt");
ck_assert_str_eq(get_tidheader_config(),
"X-Telemetry-TID: 6907c830-eed9-4ce9-81ae-76daf8d88f0f");
ck_assert(daemon_recycling_enabled_config() == true);
}
END_TEST
+176 -128
View File
@@ -22,16 +22,16 @@
#include <errno.h>
#include <stdbool.h>
#include <string.h>
#include <glib.h>
#include "nica/nc-string.h"
#include "log.h"
#include "read_oopsfile.h"
#include "src/probes/klog_scanner.h"
#include "src/probes/oops_parser.h"
char reason[1024];
GString *bt;
GString *pl;
nc_string *bt;
nc_string *pl;
void callback_func(struct oops_log_msg *msg)
{
@@ -100,26 +100,26 @@ START_TEST(watchdog_payload)
ck_assert(strstr(pl->str, "#1 dump_stack"));
ck_assert(strstr(pl->str, "#2 warn_slowpath_common"));
ck_assert(strstr(pl->str, "#3 warn_slowpath_fmt"));
ck_assert(strstr(pl->str, "#4 __queue_work"));
ck_assert(strstr(pl->str, "#4 ? __queue_work"));
ck_assert(strstr(pl->str, "#5 dev_watchdog"));
ck_assert(strstr(pl->str, "#6 dev_deactivate_queue.constprop.30"));
ck_assert(strstr(pl->str, "#6 ? dev_deactivate_queue.constprop.30"));
ck_assert(strstr(pl->str, "#7 call_timer_fn"));
ck_assert(strstr(pl->str, "#8 dev_deactivate_queue.constprop.30"));
ck_assert(strstr(pl->str, "#8 ? dev_deactivate_queue.constprop.30"));
ck_assert(strstr(pl->str, "#9 run_timer_softirq"));
ck_assert(strstr(pl->str, "#10 __do_softirq"));
ck_assert(strstr(pl->str, "#11 irq_exit"));
ck_assert(strstr(pl->str, "#12 smp_apic_timer_interrupt"));
ck_assert(strstr(pl->str, "#13 apic_timer_interrupt"));
ck_assert(strstr(pl->str, "#14 __hrtimer_start_range_ns"));
ck_assert(strstr(pl->str, "#15 cpuidle_enter_state"));
ck_assert(strstr(pl->str, "#16 cpuidle_enter_state"));
ck_assert(strstr(pl->str, "#14 ? __hrtimer_start_range_ns"));
ck_assert(strstr(pl->str, "#15 ? cpuidle_enter_state"));
ck_assert(strstr(pl->str, "#16 ? cpuidle_enter_state"));
ck_assert(strstr(pl->str, "#17 cpuidle_idle_call"));
ck_assert(strstr(pl->str, "#18 arch_cpu_idle"));
ck_assert(strstr(pl->str, "#19 cpu_startup_entry"));
ck_assert(strstr(pl->str, "#20 clockevents_register_device"));
ck_assert(strstr(pl->str, "#20 ? clockevents_register_device"));
ck_assert(strstr(pl->str, "#21 start_secondary"));
g_string_free(pl, true);
nc_string_free(pl);
}
END_TEST
@@ -141,10 +141,10 @@ START_TEST(alsa_bug_payload)
ck_assert(strstr(pl->str, "Tainted : Not tainted"));
ck_assert(strstr(pl->str, "#1 warn_on_slowpath"));
ck_assert(strstr(pl->str, "#2 vsnprintf"));
ck_assert(strstr(pl->str, "#3 _spin_unlock_irqrestore"));
ck_assert(strstr(pl->str, "#2 ? vsnprintf"));
ck_assert(strstr(pl->str, "#3 ? _spin_unlock_irqrestore"));
ck_assert(strstr(pl->str, "#10 start_kernel"));
g_string_free(pl, true);
nc_string_free(pl);
}
END_TEST
@@ -173,16 +173,16 @@ START_TEST(warning_payload)
ck_assert(strstr(pl->str, "#3 warn_slowpath_fmt"));
ck_assert(strstr(pl->str, "#4 preempt_notifier_register"));
ck_assert(strstr(pl->str, "#5 VBoxHost_RTThreadCtxHooksRegister"));
ck_assert(strstr(pl->str, "#6 update_curr"));
ck_assert(strstr(pl->str, "#7 supdrvIOCtlFast"));
ck_assert(strstr(pl->str, "#8 VBoxDrvLinuxIOCtl_4_3_28"));
ck_assert(strstr(pl->str, "#9 put_prev_entity"));
ck_assert(strstr(pl->str, "#10 do_vfs_ioctl"));
ck_assert(strstr(pl->str, "#11 pick_next_task_rt"));
ck_assert(strstr(pl->str, "#12 SyS_ioctl"));
ck_assert(strstr(pl->str, "#13 entry_SYSCALL_64_fastpath"));
ck_assert(strstr(pl->str, "#6 ? update_curr"));
ck_assert(strstr(pl->str, "#7 ? supdrvIOCtlFast"));
ck_assert(strstr(pl->str, "#8 ? VBoxDrvLinuxIOCtl_4_3_28"));
ck_assert(strstr(pl->str, "#9 ? put_prev_entity"));
ck_assert(strstr(pl->str, "#10 ? do_vfs_ioctl"));
ck_assert(strstr(pl->str, "#11 ? pick_next_task_rt"));
ck_assert(strstr(pl->str, "#12 ? SyS_ioctl"));
ck_assert(strstr(pl->str, "#13 ? entry_SYSCALL_64_fastpath"));
g_string_free(pl, true);
nc_string_free(pl);
}
END_TEST
@@ -208,21 +208,21 @@ START_TEST(warn_on_payload)
ck_assert(strstr(pl->str, "#2 warn_slowpath_common"));
ck_assert(strstr(pl->str, "#3 warn_slowpath_fmt"));
ck_assert(strstr(pl->str, "#4 dev_watchdog"));
ck_assert(strstr(pl->str, "#5 qdisc_rcu_free"));
ck_assert(strstr(pl->str, "#5 ? qdisc_rcu_free"));
ck_assert(strstr(pl->str, "#6 call_timer_fn"));
ck_assert(strstr(pl->str, "#7 qdisc_rcu_free"));
ck_assert(strstr(pl->str, "#7 ? qdisc_rcu_free"));
ck_assert(strstr(pl->str, "#8 run_timer_softirq"));
ck_assert(strstr(pl->str, "#9 __do_softirq"));
ck_assert(strstr(pl->str, "#10 irq_exit"));
ck_assert(strstr(pl->str, "#11 smp_apic_timer_interrupt"));
ck_assert(strstr(pl->str, "#12 apic_timer_interrupt"));
ck_assert(strstr(pl->str, "#13 cpuidle_enter_state"));
ck_assert(strstr(pl->str, "#14 cpuidle_enter_state"));
ck_assert(strstr(pl->str, "#13 ? cpuidle_enter_state"));
ck_assert(strstr(pl->str, "#14 ? cpuidle_enter_state"));
ck_assert(strstr(pl->str, "#15 cpuidle_enter"));
ck_assert(strstr(pl->str, "#16 cpu_startup_entry"));
ck_assert(strstr(pl->str, "#17 start_secondary"));
g_string_free(pl, true);
nc_string_free(pl);
}
END_TEST
@@ -285,22 +285,22 @@ START_TEST(sysctl2_payload)
ck_assert(strstr(pl->str, "Kernel Version : 2.6.32.53-bigmem-5-openvz #28"));
ck_assert(strstr(pl->str, "Tainted : P"));
ck_assert(strstr(pl->str, "#1 set_fail"));
ck_assert(strstr(pl->str, "#2 sysctl_check_table"));
ck_assert(strstr(pl->str, "#3 sysctl_check_lookup"));
ck_assert(strstr(pl->str, "#4 sysctl_check_table"));
ck_assert(strstr(pl->str, "#5 sysctl_check_lookup"));
ck_assert(strstr(pl->str, "#6 sysctl_check_table"));
ck_assert(strstr(pl->str, "#7 __kmalloc"));
ck_assert(strstr(pl->str, "#8 __register_sysctl_paths"));
ck_assert(strstr(pl->str, "#9 proc_register"));
ck_assert(strstr(pl->str, "#10 register_net_sysctl_table"));
ck_assert(strstr(pl->str, "#11 nf_conntrack_net_init"));
ck_assert(strstr(pl->str, "#12 setup_net"));
ck_assert(strstr(pl->str, "#13 copy_net_ns"));
ck_assert(strstr(pl->str, "#14 create_new_namespaces"));
ck_assert(strstr(pl->str, "#1 ? set_fail"));
ck_assert(strstr(pl->str, "#2 ? sysctl_check_table"));
ck_assert(strstr(pl->str, "#3 ? sysctl_check_lookup"));
ck_assert(strstr(pl->str, "#4 ? sysctl_check_table"));
ck_assert(strstr(pl->str, "#5 ? sysctl_check_lookup"));
ck_assert(strstr(pl->str, "#6 ? sysctl_check_table"));
ck_assert(strstr(pl->str, "#7 ? __kmalloc"));
ck_assert(strstr(pl->str, "#8 ? __register_sysctl_paths"));
ck_assert(strstr(pl->str, "#9 ? proc_register"));
ck_assert(strstr(pl->str, "#10 ? register_net_sysctl_table"));
ck_assert(strstr(pl->str, "#11 ? nf_conntrack_net_init"));
ck_assert(strstr(pl->str, "#12 ? setup_net"));
ck_assert(strstr(pl->str, "#13 ? copy_net_ns"));
ck_assert(strstr(pl->str, "#14 ? create_new_namespaces"));
g_string_free(pl, true);
nc_string_free(pl);
}
END_TEST
@@ -327,7 +327,7 @@ START_TEST(softlockup_payload)
ck_assert(strstr(pl->str, "#2 btrfs_tree_read_lock"));
ck_assert(strstr(pl->str, "#3 btrfs_read_lock_root_node"));
ck_assert(strstr(pl->str, "#4 btrfs_search_slot"));
ck_assert(strstr(pl->str, "#5 crypto_shash_update"));
ck_assert(strstr(pl->str, "#5 ? crypto_shash_update"));
ck_assert(strstr(pl->str, "#6 btrfs_lookup_xattr"));
ck_assert(strstr(pl->str, "#7 __btrfs_getxattr"));
ck_assert(strstr(pl->str, "#8 btrfs_getxattr"));
@@ -337,7 +337,7 @@ START_TEST(softlockup_payload)
ck_assert(strstr(pl->str, "#12 SyS_fgetxattr"));
ck_assert(strstr(pl->str, "#13 system_call_fastpath"));
g_string_free(pl, true);
nc_string_free(pl);
}
END_TEST
@@ -356,21 +356,21 @@ START_TEST(rtnl_payload)
ck_assert(strstr(pl->str, "Tainted : Not tainted"));
ck_assert(strstr(pl->str, "#1 dump_stack"));
ck_assert(strstr(pl->str, "#2 bond_set_rx_mode"));
ck_assert(strstr(pl->str, "#3 __dev_mc_add"));
ck_assert(strstr(pl->str, "#4 igmp6_group_added"));
ck_assert(strstr(pl->str, "#5 sock_def_readable"));
ck_assert(strstr(pl->str, "#6 ipv6_dev_mc_inc"));
ck_assert(strstr(pl->str, "#7 ipv6_sock_mc_join"));
ck_assert(strstr(pl->str, "#8 do_ipv6_setsockopt.isra.6"));
ck_assert(strstr(pl->str, "#9 pipe_write"));
ck_assert(strstr(pl->str, "#10 SYSC_sendto"));
ck_assert(strstr(pl->str, "#11 ipv6_setsockopt"));
ck_assert(strstr(pl->str, "#12 SyS_setsockopt"));
ck_assert(strstr(pl->str, "#13 system_call_fastpath"));
ck_assert(strstr(pl->str, "#1 ? dump_stack"));
ck_assert(strstr(pl->str, "#2 ? bond_set_rx_mode"));
ck_assert(strstr(pl->str, "#3 ? __dev_mc_add"));
ck_assert(strstr(pl->str, "#4 ? igmp6_group_added"));
ck_assert(strstr(pl->str, "#5 ? sock_def_readable"));
ck_assert(strstr(pl->str, "#6 ? ipv6_dev_mc_inc"));
ck_assert(strstr(pl->str, "#7 ? ipv6_sock_mc_join"));
ck_assert(strstr(pl->str, "#8 ? do_ipv6_setsockopt.isra.6"));
ck_assert(strstr(pl->str, "#9 ? pipe_write"));
ck_assert(strstr(pl->str, "#10 ? SYSC_sendto"));
ck_assert(strstr(pl->str, "#11 ? ipv6_setsockopt"));
ck_assert(strstr(pl->str, "#12 ? SyS_setsockopt"));
ck_assert(strstr(pl->str, "#13 ? system_call_fastpath"));
g_string_free(pl, true);
nc_string_free(pl);
}
END_TEST
@@ -400,7 +400,7 @@ START_TEST(kernel_null_pointer_payload)
ck_assert(strstr(pl->str, "#6 sys_inotify_rm_watch"));
ck_assert(strstr(pl->str, "#7 system_call_fastpath"));
g_string_free(pl, true);
nc_string_free(pl);
}
END_TEST
@@ -432,7 +432,7 @@ START_TEST(kernel_bug_payload)
ck_assert(strstr(pl->str, "#8 ptregscall_common"));
ck_assert(strstr(pl->str, "#9 sysret_signal"));
g_string_free(pl, true);
nc_string_free(pl);
}
END_TEST
@@ -454,51 +454,51 @@ START_TEST(irq_payload)
ck_assert(strstr(pl->str, "#1 dump_stack"));
ck_assert(strstr(pl->str, "#2 __report_bad_irq"));
ck_assert(strstr(pl->str, "#3 note_interrupt"));
ck_assert(strstr(pl->str, "#4 __do_softirq"));
ck_assert(strstr(pl->str, "#4 ? __do_softirq"));
ck_assert(strstr(pl->str, "#5 handle_irq_event_percpu"));
ck_assert(strstr(pl->str, "#6 handle_irq_event"));
ck_assert(strstr(pl->str, "#7 handle_nested_irq"));
ck_assert(strstr(pl->str, "#7 ? handle_nested_irq"));
ck_assert(strstr(pl->str, "#8 handle_level_irq"));
ck_assert(strstr(pl->str, "#9 do_IRQ"));
ck_assert(strstr(pl->str, "#10 common_interrupt"));
ck_assert(strstr(pl->str, "#11 __do_softirq"));
ck_assert(strstr(pl->str, "#12 handle_irq"));
ck_assert(strstr(pl->str, "#13 irq_exit"));
ck_assert(strstr(pl->str, "#14 do_IRQ"));
ck_assert(strstr(pl->str, "#15 pci_write"));
ck_assert(strstr(pl->str, "#16 common_interrupt"));
ck_assert(strstr(pl->str, "#17 audit_make_tree"));
ck_assert(strstr(pl->str, "#18 usbdev_ioctl"));
ck_assert(strstr(pl->str, "#19 _raw_spin_unlock_irqrestore"));
ck_assert(strstr(pl->str, "#20 __setup_irq"));
ck_assert(strstr(pl->str, "#21 kmem_cache_alloc_trace"));
ck_assert(strstr(pl->str, "#22 vsnprintf"));
ck_assert(strstr(pl->str, "#23 request_threaded_irq"));
ck_assert(strstr(pl->str, "#24 usb_hcd_platform_shutdown"));
ck_assert(strstr(pl->str, "#25 request_threaded_irq"));
ck_assert(strstr(pl->str, "#26 usb_add_hcd"));
ck_assert(strstr(pl->str, "#27 usb_hcd_pci_probe"));
ck_assert(strstr(pl->str, "#28 rpm_resume"));
ck_assert(strstr(pl->str, "#29 pci_device_probe"));
ck_assert(strstr(pl->str, "#30 driver_probe_device"));
ck_assert(strstr(pl->str, "#31 pci_match_device"));
ck_assert(strstr(pl->str, "#32 __driver_attach"));
ck_assert(strstr(pl->str, "#33 driver_probe_device"));
ck_assert(strstr(pl->str, "#34 bus_for_each_dev"));
ck_assert(strstr(pl->str, "#35 driver_attach"));
ck_assert(strstr(pl->str, "#36 driver_probe_device"));
ck_assert(strstr(pl->str, "#37 bus_add_driver"));
ck_assert(strstr(pl->str, "#38 pci_match_id"));
ck_assert(strstr(pl->str, "#39 driver_register"));
ck_assert(strstr(pl->str, "#40 ohci_hcd_mod_init"));
ck_assert(strstr(pl->str, "#41 ohci_hcd_mod_init"));
ck_assert(strstr(pl->str, "#42 __pci_register_driver"));
ck_assert(strstr(pl->str, "#43 uhci_hcd_init"));
ck_assert(strstr(pl->str, "#44 do_one_initcall"));
ck_assert(strstr(pl->str, "#45 kernel_init_freeable"));
ck_assert(strstr(pl->str, "#46 do_early_param"));
ck_assert(strstr(pl->str, "#9 ? do_IRQ"));
ck_assert(strstr(pl->str, "#10 ? common_interrupt"));
ck_assert(strstr(pl->str, "#11 ? __do_softirq"));
ck_assert(strstr(pl->str, "#12 ? handle_irq"));
ck_assert(strstr(pl->str, "#13 ? irq_exit"));
ck_assert(strstr(pl->str, "#14 ? do_IRQ"));
ck_assert(strstr(pl->str, "#15 ? pci_write"));
ck_assert(strstr(pl->str, "#16 ? common_interrupt"));
ck_assert(strstr(pl->str, "#17 ? audit_make_tree"));
ck_assert(strstr(pl->str, "#18 ? usbdev_ioctl"));
ck_assert(strstr(pl->str, "#19 ? _raw_spin_unlock_irqrestore"));
ck_assert(strstr(pl->str, "#20 ? __setup_irq"));
ck_assert(strstr(pl->str, "#21 ? kmem_cache_alloc_trace"));
ck_assert(strstr(pl->str, "#22 ? vsnprintf"));
ck_assert(strstr(pl->str, "#23 ? request_threaded_irq"));
ck_assert(strstr(pl->str, "#24 ? usb_hcd_platform_shutdown"));
ck_assert(strstr(pl->str, "#25 ? request_threaded_irq"));
ck_assert(strstr(pl->str, "#26 ? usb_add_hcd"));
ck_assert(strstr(pl->str, "#27 ? usb_hcd_pci_probe"));
ck_assert(strstr(pl->str, "#28 ? rpm_resume"));
ck_assert(strstr(pl->str, "#29 ? pci_device_probe"));
ck_assert(strstr(pl->str, "#30 ? driver_probe_device"));
ck_assert(strstr(pl->str, "#31 ? pci_match_device"));
ck_assert(strstr(pl->str, "#32 ? __driver_attach"));
ck_assert(strstr(pl->str, "#33 ? driver_probe_device"));
ck_assert(strstr(pl->str, "#34 ? bus_for_each_dev"));
ck_assert(strstr(pl->str, "#35 ? driver_attach"));
ck_assert(strstr(pl->str, "#36 ? driver_probe_device"));
ck_assert(strstr(pl->str, "#37 ? bus_add_driver"));
ck_assert(strstr(pl->str, "#38 ? pci_match_id"));
ck_assert(strstr(pl->str, "#39 ? driver_register"));
ck_assert(strstr(pl->str, "#40 ? ohci_hcd_mod_init"));
ck_assert(strstr(pl->str, "#41 ? ohci_hcd_mod_init"));
ck_assert(strstr(pl->str, "#42 ? __pci_register_driver"));
ck_assert(strstr(pl->str, "#43 ? uhci_hcd_init"));
ck_assert(strstr(pl->str, "#44 ? do_one_initcall"));
ck_assert(strstr(pl->str, "#45 ? kernel_init_freeable"));
ck_assert(strstr(pl->str, "#46 ? do_early_param"));
g_string_free(pl, true);
nc_string_free(pl);
}
END_TEST
@@ -521,16 +521,16 @@ START_TEST(general_protection_fault_payload)
ck_assert(strstr(pl->str, "Modules : tun 8021q garp mrp bridge stp llc bonding ipt_REJECT xt_comment xt_multiport"));
ck_assert(strstr(pl->str, "lpc_ich iw_cm mfd_core nfsd auth_rpcgss wmi nfs_acl lockd ipmi_si ipmi_devintf"));
ck_assert(strstr(pl->str, "#1 alloc_pipe_info"));
ck_assert(strstr(pl->str, "#1 ? alloc_pipe_info"));
ck_assert(strstr(pl->str, "#2 alloc_pipe_info"));
ck_assert(strstr(pl->str, "#3 create_pipe_files"));
ck_assert(strstr(pl->str, "#4 __do_pipe_flags"));
ck_assert(strstr(pl->str, "#5 SyS_pipe2"));
ck_assert(strstr(pl->str, "#6 __audit_syscall_exit"));
ck_assert(strstr(pl->str, "#6 ? __audit_syscall_exit"));
ck_assert(strstr(pl->str, "#7 SyS_pipe"));
ck_assert(strstr(pl->str, "#8 system_call_fastpath"));
g_string_free(pl, true);
nc_string_free(pl);
}
END_TEST
@@ -552,7 +552,7 @@ START_TEST(double_fault_payload)
ck_assert(strstr(pl->str, "Modules : nls_utf8 vfat fat sit tunnel4 ext2 fuse ip6table_filter ip6_tables ebtable_nat"));
ck_assert(strstr(pl->str, "joydev sdhci_pci sdhci usb_storage mmc_core video output [last unloaded: speedstep_lib]"));
g_string_free(pl, true);
nc_string_free(pl);
}
END_TEST
@@ -571,7 +571,7 @@ START_TEST(bad_page_map_payload)
ck_assert(strstr(pl->str, "Kernel Version : 3.12-1-amd64 #1 Debian 3.12.9-1"));
ck_assert(strstr(pl->str, "Tainted : Not tainted"));
g_string_free(pl, true);
nc_string_free(pl);
}
END_TEST
@@ -594,31 +594,78 @@ START_TEST(bug_kernel_handle_payload)
ck_assert(strstr(pl->str, "Modules : nf_conntrack_netbios_ns nf_conntrack_broadcast ipt_MASQUERADE be2iscsi"));
ck_assert(strstr(pl->str, "drm_kms_helper firewire_ohci drm firewire_core tg3 crc_itu_t i2c_core video usb_storage sunrpc"));
ck_assert(strstr(pl->str, "#1 _nv007312rm"));
ck_assert(strstr(pl->str, "#2 _nv007847rm"));
ck_assert(strstr(pl->str, "#3 _nv004049rm"));
ck_assert(strstr(pl->str, "#4 _nv004049rm"));
ck_assert(strstr(pl->str, "#5 _nv010019rm"));
ck_assert(strstr(pl->str, "#6 _nv014983rm"));
ck_assert(strstr(pl->str, "#7 _nv001097rm"));
ck_assert(strstr(pl->str, "#8 rm_init_adapter"));
ck_assert(strstr(pl->str, "#1 ? _nv007312rm"));
ck_assert(strstr(pl->str, "#2 ? _nv007847rm"));
ck_assert(strstr(pl->str, "#3 ? _nv004049rm"));
ck_assert(strstr(pl->str, "#4 ? _nv004049rm"));
ck_assert(strstr(pl->str, "#5 ? _nv010019rm"));
ck_assert(strstr(pl->str, "#6 ? _nv014983rm"));
ck_assert(strstr(pl->str, "#7 ? _nv001097rm"));
ck_assert(strstr(pl->str, "#8 ? rm_init_adapter"));
ck_assert(strstr(pl->str, "#9 ? nv_kern_open"));
ck_assert(strstr(pl->str, "#10 ? chrdev_open"));
ck_assert(strstr(pl->str, "#11 ? do_dentry_open"));
ck_assert(strstr(pl->str, "#12 ? cdev_put"));
ck_assert(strstr(pl->str, "#13 ? finish_open"));
ck_assert(strstr(pl->str, "#14 ? do_last"));
ck_assert(strstr(pl->str, "#15 ? inode_permission"));
ck_assert(strstr(pl->str, "#16 ? link_path_walk"));
ck_assert(strstr(pl->str, "#17 ? path_openat"));
ck_assert(strstr(pl->str, "#18 ? do_filp_open"));
ck_assert(strstr(pl->str, "#19 ? __alloc_fd"));
ck_assert(strstr(pl->str, "#20 ? do_sys_open"));
ck_assert(strstr(pl->str, "#21 ? __audit_syscall_entry"));
ck_assert(strstr(pl->str, "#22 ? sys_open"));
ck_assert(strstr(pl->str, "#23 ? system_call_fastpath"));
nc_string_free(pl);
}
END_TEST
START_TEST(bug_kernel_handle_payload_new_format)
{
char *oopsfile = NULL;
oopsfile = TESTOOPSDIR "/bug_kernel_handle_new.txt";
setup_payload(oopsfile);
telem_log(LOG_ERR, "Bug kernel handle backtrace: %s\n", pl->str);
ck_assert(pl->len > 0);
ck_assert_str_eq(reason, "BUG: unable to handle kernel paging request at 0000000000002658");
ck_assert(strstr(pl->str, "Kernel Version : 3.7.9-201.fc18.x86_64 #1 Apple Inc. MacBookPro6,2/Mac-F22586C8"));
ck_assert(strstr(pl->str, "Tainted : PF"));
ck_assert(strstr(pl->str, "Modules : nf_conntrack_netbios_ns nf_conntrack_broadcast ipt_MASQUERADE be2iscsi"));
ck_assert(strstr(pl->str, "drm_kms_helper firewire_ohci drm firewire_core tg3 crc_itu_t i2c_core video usb_storage sunrpc"));
ck_assert(strstr(pl->str, "#1 ? _nv007312rm"));
ck_assert(strstr(pl->str, "#2 ? _nv007847rm"));
ck_assert(strstr(pl->str, "#3 ? _nv004049rm"));
ck_assert(strstr(pl->str, "#4 ? _nv004049rm"));
ck_assert(strstr(pl->str, "#5 ? _nv010019rm"));
ck_assert(strstr(pl->str, "#6 ? _nv014983rm"));
ck_assert(strstr(pl->str, "#7 ? _nv001097rm"));
ck_assert(strstr(pl->str, "#8 ? rm_init_adapter"));
ck_assert(strstr(pl->str, "#9 nv_kern_open"));
ck_assert(strstr(pl->str, "#10 chrdev_open"));
ck_assert(strstr(pl->str, "#11 do_dentry_open"));
ck_assert(strstr(pl->str, "#12 cdev_put"));
ck_assert(strstr(pl->str, "#13 finish_open"));
ck_assert(strstr(pl->str, "#14 do_last"));
ck_assert(strstr(pl->str, "#15 inode_permission"));
ck_assert(strstr(pl->str, "#10 ? chrdev_open"));
ck_assert(strstr(pl->str, "#11 ? do_dentry_open"));
ck_assert(strstr(pl->str, "#12 ? cdev_put"));
ck_assert(strstr(pl->str, "#13 ? finish_open"));
ck_assert(strstr(pl->str, "#14 ? do_last"));
ck_assert(strstr(pl->str, "#15 ? inode_permission"));
ck_assert(strstr(pl->str, "#16 link_path_walk"));
ck_assert(strstr(pl->str, "#17 path_openat"));
ck_assert(strstr(pl->str, "#18 do_filp_open"));
ck_assert(strstr(pl->str, "#19 __alloc_fd"));
ck_assert(strstr(pl->str, "#20 do_sys_open"));
ck_assert(strstr(pl->str, "#21 __audit_syscall_entry"));
ck_assert(strstr(pl->str, "#22 sys_open"));
ck_assert(strstr(pl->str, "#23 system_call_fastpath"));
ck_assert(strstr(pl->str, "#18 ? do_filp_open"));
ck_assert(strstr(pl->str, "#19 ? __alloc_fd"));
ck_assert(strstr(pl->str, "#20 ? do_sys_open"));
ck_assert(strstr(pl->str, "#21 ? __audit_syscall_entry"));
ck_assert(strstr(pl->str, "#22 ? sys_open"));
ck_assert(strstr(pl->str, "#23 ? system_call_fastpath"));
g_string_free(pl, true);
nc_string_free(pl);
}
END_TEST
@@ -648,6 +695,7 @@ Suite *config_suite(void)
tcase_add_test(t, double_fault_payload);
tcase_add_test(t, bad_page_map_payload);
tcase_add_test(t, bug_kernel_handle_payload);
tcase_add_test(t, bug_kernel_handle_payload_new_format);
//TODO fix
//tcase_add_test(t, badness_payload);
+5 -11
View File
@@ -24,12 +24,10 @@ dist_check_SCRIPTS = \
%C%_check_config_CFLAGS = \
$(AM_CFLAGS) \
@CHECK_CFLAGS@ \
@GLIB_CFLAGS@
@CHECK_CFLAGS@
%C%_check_config_LDADD = \
@CHECK_LIBS@ \
@GLIB_LIBS@ \
$(top_builddir)/src/libtelem-shared.la
%C%_check_daemon_SOURCES = \
@@ -40,11 +38,9 @@ dist_check_SCRIPTS = \
%C%_check_daemon_CFLAGS = \
$(AM_CFLAGS) \
@CHECK_CFLAGS@ \
@GLIB_CFLAGS@ \
@CURL_CFLAGS@
%C%_check_daemon_LDADD = \
@CHECK_LIBS@ \
@GLIB_LIBS@ \
@CURL_LIBS@ \
$(top_builddir)/src/libtelem-shared.la
@@ -61,6 +57,7 @@ endif
%D%/read_oopsfile.h \
%D%/read_oopsfile.c \
%D%/check_probes.c \
src/nica/nc-string.c \
src/probes/klog_scanner.c \
src/probes/klog_scanner.h \
src/probes/oops_parser.c \
@@ -72,6 +69,7 @@ EXTRA_DIST += \
%D%/oops_test_files/badness.txt \
%D%/oops_test_files/bad_page_map.txt \
%D%/oops_test_files/bug_kernel_handle.txt \
%D%/oops_test_files/bug_kernel_handle_new.txt \
%D%/oops_test_files/double_fault_DONE.txt \
%D%/oops_test_files/double_fault.txt \
%D%/oops_test_files/general_protection_fault.txt \
@@ -94,11 +92,9 @@ EXTRA_DIST += \
%C%_check_probes_CFLAGS = \
$(AM_CFLAGS) \
@CHECK_CFLAGS@ \
@GLIB_CFLAGS@
@CHECK_CFLAGS@
%C%_check_probes_LDADD = \
@CHECK_LIBS@ \
@GLIB_LIBS@ \
$(top_builddir)/src/libtelem-shared.la
if LOG_SYSTEMD
@@ -116,11 +112,9 @@ endif
%C%_check_libtelemetry_CFLAGS = \
$(AM_CFLAGS) \
@CHECK_CFLAGS@ \
@GLIB_CFLAGS@
@CHECK_CFLAGS@
%C%_check_libtelemetry_LDADD = \
@CHECK_LIBS@ \
@GLIB_LIBS@ \
$(top_builddir)/src/libtelemetry.la \
$(top_builddir)/src/libtelem-shared.la
@@ -0,0 +1,51 @@
<4>[ 68764.975947] BUG: unable to handle kernel paging request at 0000000000002658
<4>[ 68764.975947] IP: [<ffffffffa07f92e5>] _nv007245rm+0x54/0xc4 [nvidia]
<4>[ 68764.975947] PGD 22d005067 PUD 22d006067 PMD 0
<4>[ 68764.975947] Oops: 0000 [#1] SMP
<4>[ 68764.975947] Modules linked in: nf_conntrack_netbios_ns nf_conntrack_broadcast ipt_MASQUERADE be2iscsi iscsi_boot_sysfs bnx2i cnic uio cxgb4i cxgb4 cxgb3i cxgb3 mdio ip6table_mangle libcxgbi ib_iser rdma_cm ib_addr iw_cm ib_cm ib_sa ib_mad ib_core iscsi_tcp libiscsi_tcp ip6t_REJECT libiscsi scsi_transport_iscsi nf_conntrack_ipv6 nf_defrag_ipv6 iptable_nat nf_nat_ipv4 nf_nat iptable_mangle nf_conntrack_ipv4 nf_defrag_ipv4 xt_conntrack nf_conntrack ebtable_filter ebtables rfcomm ip6table_filter ip6_tables bnep nls_utf8 hfsplus binfmt_misc snd_hda_codec_hdmi arc4 brcmsmac cordic brcmutil mac80211 cfg80211 joydev iTCO_wdt iTCO_vendor_support nvidia(POF) coretemp applesmc input_polldev microcode uvcvideo i2c_i801 intel_ips snd_hda_codec_cirrus videobuf2_vmalloc videobuf2_memops videobuf2_core videodev btusb media bluetooth snd_hda_intel snd_hda_codec rfkill snd_hwdep bcm5974 snd_seq snd_seq_device snd_pcm lpc_ich mfd_core bcma snd_page_alloc snd_timer vhost_net tun snd macvtap macvlan soundcore kvm_intel kvm apple_gmux apple_bl uinput crc32c_intel i915 ghash_clmulni_intel i2c_algo_bit drm_kms_helper firewire_ohci drm firewire_core tg3 crc_itu_t i2c_core video usb_storage sunrpc
<4>[ 68764.975947] CPU 2
<4>[ 68764.975947] Pid: 1059, comm: Xorg Tainted: PF O 3.7.9-201.fc18.x86_64 #1 Apple Inc. MacBookPro6,2/Mac-F22586C8
<4>[ 68764.975947] RIP: 0010:[<ffffffffa07f92e5>] [<ffffffffa07f92e5>] _nv007245rm+0x54/0xc4 [nvidia]
<4>[ 68764.975947] RSP: 0018:ffff88022d0639b8 EFLAGS: 00010282
<4>[ 68764.975947] RAX: 0000000000000000 RBX: ffff88022d190008 RCX: 0000000000000000
<4>[ 68764.975947] RDX: 0000000000000000 RSI: 0000000000000015 RDI: 0000000000000000
<4>[ 68764.975947] RBP: ffff88022d30df08 R08: 0000000000000002 R09: ffff88023f342508
<4>[ 68764.975947] R10: ffff88022d951030 R11: 0000000000000000 R12: 0000000000000000
<4>[ 68764.975947] R13: ffff88023f3b0008 R14: 0000000000000000 R15: ffff88022da96008
<4>[ 68764.975947] FS: 00007f771f4f2940(0000) GS:ffff88024fc80000(0000) knlGS:0000000000000000
<4>[ 68764.975947] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
<4>[ 68764.975947] CR2: 0000000000002658 CR3: 000000022d004000 CR4: 00000000000007e0
<4>[ 68764.975947] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
<4>[ 68764.975947] DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400
<4>[ 68764.975947] Process Xorg
<4>[ 68764.975947] Stack:
<4>[ 68764.975947] ffff88022d33a008 ffff88022d33a008 0000000000000002 ffff88023f3b0008
<4>[ 68764.975947] ffff88022d190008 ffffffffa07deec8 ffff88023d67b008 ffff88022d190008
<4>[ 68764.975947] ffff88023f3b0008 ffff88023f3b0008 ffff88022da94808 ffffffffa083e77f
<4>[ 68764.975947] Call Trace:
<4>[ 68764.975947] ? _nv007312rm+0x7f8/0x938 [nvidia]
<4>[ 68764.975947] ? _nv007847rm+0x80/0x276 [nvidia]
<4>[ 68764.975947] ? _nv004049rm+0x85fb/0xaef0 [nvidia]
<4>[ 68764.975947] ? _nv004049rm+0x705f/0xaef0 [nvidia]
<4>[ 68764.975947] ? _nv010019rm+0x25/0x40 [nvidia]
<4>[ 68764.975947] ? _nv014983rm+0x7c9/0x943 [nvidia]
<4>[ 68764.975947] ? _nv001097rm+0x42b/0x661 [nvidia]
<4>[ 68764.975947] ? rm_init_adapter+0xac/0x146 [nvidia]
<4>[ 68764.975947] nv_kern_open+0x4b4/0x800 [nvidia]
<4>[ 68764.975947] ? chrdev_open+0x9b/0x180
<4>[ 68764.975947] ? do_dentry_open+0x203/0x290
<4>[ 68764.975947] ? cdev_put+0x30/0x30
<4>[ 68764.975947] ? finish_open+0x35/0x50
<4>[ 68764.975947] ? do_last+0x6de/0xe00
<4>[ 68764.975947] ? inode_permission+0x18/0x50
<4>[ 68764.975947] link_path_walk+0x77/0x870
<4>[ 68764.975947] path_openat+0xbc/0x4d0
<4>[ 68764.975947] ? do_filp_open+0x41/0xa0
<4>[ 68764.975947] ? __alloc_fd+0x42/0x110
<4>[ 68764.975947] ? do_sys_open+0xf4/0x1e0
<4>[ 68764.975947] ? __audit_syscall_entry+0xcc/0x300
<4>[ 68764.975947] ? sys_open+0x21/0x30
<4>[ 68764.975947] ? system_call_fastpath+0x16/0x1b
<4>[ 68764.975947] Code: e0 05 00 00 00 00 00 00 41 bc 00 00 00 00 eb 18 44 89 e2 48 89 de 4c 89 ef ff 93 48 1a 00 00 01 83 e0 05 00 00 41 ff c4 4c 89 f7 <41> ff 96 58 26 00 00 44 39 e0 77 d9 ba 74 06 10 00 be 00 00 00
<4>[ 68764.975947] RIP [<ffffffffa07f92e5>] _nv007245rm+0x54/0xc4 [nvidia]
<4>[ 68764.975947] RSP <ffff88022d0639b8>