Compare commits

..
36 Commits
Author SHA1 Message Date
Alex Jaramillo 46f2e0c8cb Release v2.1.1 2019-02-25 21:14:14 +00:00
Juro Bystricky ffc0554346 pythonprobe: fix folders
Python3 expects two "well known" folders to exist in order to generate exception
payloads:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

define MAX_PAYLOAD_SIZE 8192
define MAX_PAYLOAD_LENGTH 8192

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

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

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

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

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

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

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

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

This reverts commit d1425173a9.

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

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

Signed-off-by: Alex Jaramillo <alex.v.jaramillo@intel.com>
2018-07-27 15:25:01 -07:00
78 changed files with 2960 additions and 1679 deletions
+7 -2
View File
@@ -33,7 +33,8 @@ build-aux/m4/ltversion.m4
*.trs
/cscope.*
/tags
telemd
telemprobd
telempostd
autoscan.log
configure.scan
tprobe
@@ -41,9 +42,9 @@ hprobe
bertprobe
crashprobe
journalprobe
pythonprobe
telem-record-gen
klogscanner
oopsprobe
pstoreclean
pstoreprobe
telem_journal
@@ -55,7 +56,11 @@ src/gcc.out
tests/demo
tests/check_config
tests/check_daemon
tests/check_journal
tests/check_libtelemetry
tests/check_ncb64
tests/check_postd
tests/check_probd
tests/check_probes
src/data/*.path
src/data/*.service
+41 -23
View File
@@ -1,3 +1,5 @@
[![Build Status](https://travis-ci.org/clearlinux/telemetrics-client.svg?branch=master)](https://travis-ci.org/clearlinux/telemetrics-client)
Telemetrics-client
==================
@@ -11,7 +13,10 @@ component includes:
- a library, libtelemetry, that telemetrics probes use to create telemetrics
records and send them to the daemon for further processing.
- a daemon, telemd, that prepares the records to send to a telemetrics server
- telemprobd, a daemon that handles communication with probes and forwards records
to a second daemon.
- telempostd, this daemon sends telemetry records to a telemetrics server
(not included in this source tree), or spools the records on disk in case
it's unable to successfully deliver them.
@@ -30,7 +35,7 @@ Build dependencies
- 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.
socket/path activation of telemprobd and telempostd by systemd.
Build and installation
@@ -60,31 +65,38 @@ and modify the /etc version.
Descriptions of config options are listed below in the Usage section.
Starting the daemon
Starting the client
---------------------
If the client was compiled with systemd support the respective activation units
should be already in place (after a ```make install``` invocation). In this case
the client wil start automatically when data is made available to it. i.e. when
executing an ```/usr/bin/hprobe``` command.
Method 1 (recommended):
```{r, engine='bash', count_lines}
systemctl start telemd.socket telemd.path
telemctl start
```
Note: the above invocation technically readies the service for both socket and
path activation, so you may not see the daemon start right away.
path activation, so you may not see an "active" status.
Method 2:
```{r, engine='bash', count_lines}
systemctl start telemd.service
systemctl start telemprobd.service
systemctl start telempostd.service
```
Method 3:
```{r, engine='bash', count_lines}
telemd &
telemprobd &
telempostd &
```
Configure the daemon to autostart at boot
Configure the client to autostart at boot
---------------------
Method 1 (recommended):
@@ -92,22 +104,23 @@ Method 1 (recommended):
Enable the socket-activated service and path unit:
```{r, engine='bash', count_lines}
systemctl enable telemd.socket telemd.path
systemctl enable telemprobd.socket telempostd.path
```
Method 2:
Enable the service itself, which automatically enables the socket and path
Enable services, which automatically enables the socket and path
units as well:
```{r, engine='bash', count_lines}
systemctl enable telemd.service
systemctl enable telemprobd.service
systemctl enable telempostd.service
```
Usage
---------------------
Once the daemon is running, the telemetrics probes will be ready to use.
Once the client is running, the telemetrics probes will be ready to use.
Available probes:
@@ -117,11 +130,11 @@ 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:
The client uses the following configuration options from the configuration file:
* server: This specifies the web server that the daemon sends the telemetry records to
* server: This specifies the web server that the telempostd sends the telemetry records to
* socket_path: This specifies the path of the unix domain socket that the
daemon listens on for connections from the probes
telemprobd listens on for connections from the probes
* spool_dir: This config option is related to spooling. If the daemon is not
able to send the telemetry records to the backend server due to reasons such
as the network availability, then it stores the records in a spool directory.
@@ -131,7 +144,7 @@ The daemon uses the following configuration options from the configuration file:
```{r, engine='bash', count_lines}
mkdir -p /var/spool/telemetry
chown -R telemetry:telemetry /var/spool/telemetry
systemctl restart telemd.service
systemctl restart telemprobd.service
```
* record_expiry: This is the time in minutes after which the records in the
@@ -231,17 +244,17 @@ you can opt in by creating a static machine id file named
# echo "unique machine ID" > /etc/telemetrics/opt-in-static-machine-id
```
The telemetry daemon reads, at most, the first 32 characters from this file
The telemetry client reads, at most, the first 32 characters from this file
uses it for the machine id. You can put a string like 'my-machine-name' in this
file to easily identify your machine. Restart telemd for the machine id
file to easily identify your machine. Restart telemprobd for the machine id
changes to take effect by running:
```{r, engine='bash', count_lines}
# systemctl restart telemd.service
# systemctl restart telemprobd.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
and restarting the client. You can do a quick test to check that your
machine-id has changed by running "hprobe" and verifying that a record has
landed on your backend telemetrics server, with the specified machine id.
@@ -271,18 +284,18 @@ developers not always have access to the backend in these case users can leverag
features added for local debugging. The following is a list of steps to enable
local debug:
* *Enabling record retention*: this step configures telemd to keep
* *Enabling record retention*: this step configures telempostd to keep
copies of telemetry records locally. To enable record retention set the value of
```record_retention_enabled``` from ```false``` to ```true```. Optionally set
```record_server_delivery_enabled`` to ```false``` to keep records local only.
Remember to restart the daemon after configuration values are updated
Remember to restart the client after configuration values are updated
(```telemctl restart```).
* *Creating a record*: run ```hprobe``` command to create a record for the purposes
of this step by step guide. Once we have the record or records that you need to
capture locally you can display the data.
* *Displaying record metadata*: telemd keeps metadata of any valid record,
* *Displaying record metadata*: telempostd keeps metadata of any valid record,
to display this data a new option to telemctl was added ```telemctl journal```.
Assuming that the last record created was the record from previous step `hprobe` we
can use `tail -n 1` to print the last created record only, i.e.
@@ -310,3 +323,8 @@ $ sudo telemctl journal --record_id a19a0d41ba16788881e274b19b8a1be4 --include_r
$ org.clearlinux/hello/world Mon 2018-04-02 17:48:01 UTC a19a0d41ba16788881e274b19b8a1be4 5de9de8d5f3c6a7d445d75ba01cc3322 60c014cd-4693-40f1-b334-548cd932949b
$ hello
```
## Security Disclosures
To report a security issue or receive security advisories please follow procedures
in this [link](https://01.org/security).
-1
View File
@@ -24,7 +24,6 @@ AM_CPPFLAGS = \
-DABSTOPSRCDIR=\"$(abs_top_srcdir)\" \
-DDATADIR=\"$(datadir)\" \
-DLOCALSTATEDIR=\"$(localstatedir)\" \
-DKERNELOOPSDIR=\"$(localstatedir)/cache/telemetry/oops\" \
-DPSTOREDIR=\"$(localstatedir)/cache/telemetry/pstore\" \
-DTESTOOPSDIR=\"$(top_srcdir)/tests/oops_test_files\"
+2 -2
View File
@@ -2,13 +2,13 @@
# Process this file with autoconf to produce a configure script.
AC_PREREQ([2.69])
AC_INIT([telemetrics-client], [1.16.2], [https://clearlinux.org/])
AC_INIT([telemetrics-client], [2.1.1], [https://clearlinux.org/])
AC_CONFIG_AUX_DIR([build-aux])
AM_INIT_AUTOMAKE([1.14 -Wall -Werror -Wno-extra-portability foreign subdir-objects])
AM_SILENT_RULES([yes])
LT_INIT([disable-static])
AC_CONFIG_MACRO_DIRS([build-aux/m4])
AC_CONFIG_SRCDIR([src/main.c])
AC_CONFIG_SRCDIR([src/probe.c])
AC_CONFIG_HEADERS([config.h])
# Checks for programs.
+3 -3
View File
@@ -1,9 +1,9 @@
## Notes on manual pages
The manual pages are generated using `ronn(1)`. To recreate them,
run `make manpages` in the toplevel folder. If you want to edit
the documentation for upstream changes, make sure to edit the `*.md`
The manual pages are generated using `rst2man.py`. To recreate them,
run `make manpages` in the toplevel folder. If you want to edit
the documentation for upstream changes, make sure to edit the `*.rst`
files and not the shipped nroff output files.
+2 -1
View File
@@ -1,6 +1,7 @@
MANPAGES = \
docs/man/telemctl.1 \
docs/man/telemd.1 \
docs/man/telemprobd.1 \
docs/man/telempostd.1 \
docs/man/telem-record-gen.1 \
docs/man/telemetry.3 \
docs/man/telemetrics.conf.5
+5 -2
View File
@@ -35,10 +35,10 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
\fBtelem\-record\-gen\fP <options>
.SH DESCRIPTION
.sp
Generate and attempt to send a telemetry record to \fBtelemd\fP(1). This
Generate and attempt to send a telemetry record to \fBtelemprobd\fP(1). This
program can be used to create a telemetry record from a shell script
or from another program using e.g. \fBsystem()\fP\&. The program connects
to a local \fBtelemd\fP(1) daemon to spool the record for delivery to
to a local \fBtelemprobd\fP(1) daemon to spool the record for delivery to
the actual collection service.
.sp
The payload can be provided as standard input, as a file with the
@@ -77,6 +77,9 @@ File to read payload from.
.IP \(bu 2
\fB\-R\fP, \fB\-\-record\-version\fP <version>:
Version number for format of payload (default 1).
.IP \(bu 2
\fB\-e\fP, \fB\-\-event\-id\fP <version>:
Event id to use in the record. If not provided a randomly generated id will be assigned to record.
.UNINDENT
.UNINDENT
.UNINDENT
+6 -3
View File
@@ -19,10 +19,10 @@ SYNOPSIS
DESCRIPTION
===========
Generate and attempt to send a telemetry record to ``telemd``\(1). This
Generate and attempt to send a telemetry record to ``telemprobd``\(1). This
program can be used to create a telemetry record from a shell script
or from another program using e.g. ``system()``. The program connects
to a local ``telemd``\(1) daemon to spool the record for delivery to
to a local ``telemprobd``\(1) daemon to spool the record for delivery to
the actual collection service.
The payload can be provided as standard input, as a file with the
@@ -62,6 +62,10 @@ OPTIONS
* ``-R``, ``--record-version`` <version>:
Version number for format of payload (default 1).
* ``-e``, ``--event-id`` <version>:
Event id to use in the record. If not provided a randomly generated id will be assigned to record.
RETURN VALUES
=============
@@ -75,4 +79,3 @@ SEE ALSO
* ``telemd``\(1)
* https://github.com/clearlinux/telemetrics-client
* https://clearlinux.org/documentation/
+4 -2
View File
@@ -38,8 +38,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.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.
restart, or stop \fBtelemprobd\fP(1) and \fBtelempostd\fP(1), or to opt\-in or opt\-out of telemetry delivery of records to a central telemetry service.
.SH OPTIONS
.INDENT 0.0
.INDENT 3.5
@@ -55,6 +54,9 @@ Opts in to telemetry, and starts telemetry services. The opt\-out file
\fBopt\-out\fP:
Opts out of telemetry, and stops telemetry services. The opt\-out file
\fB/etc/telemetrics/opt\-out\fP is created.
.IP \(bu 2
\fBis\-active\fP:
Checks if telemetry client daemons are active (telemprobd and telempostd).
.UNINDENT
.UNINDENT
.UNINDENT
+6 -5
View File
@@ -22,8 +22,7 @@ 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.
restart, or stop ``telemprobd``\(1) and ``telempostd``\(1), or to opt-in or opt-out of telemetry delivery of records to a central telemetry service.
OPTIONS
@@ -32,14 +31,17 @@ OPTIONS
* ``start``|``stop``|``restart``:
Starts, stops or restarts all running telemetry services.
* ``opt-in``:
* ``opt-in``:
Opts in to telemetry, and starts telemetry services. The opt-out file
``/etc/telemetrics/opt-out`` is removed.
* ``opt-out``:
* ``opt-out``:
Opts out of telemetry, and stops telemetry services. The opt-out file
``/etc/telemetrics/opt-out`` is created.
* ``is-active``:
Checks if telemetry client daemons are active (telemprobd and telempostd).
RETURN VALUES
=============
@@ -53,4 +55,3 @@ SEE ALSO
* ``telemd``\(1)
* https://github.com/clearlinux/telemetrics-client
* https://clearlinux.org/documentation/
+6 -5
View File
@@ -37,8 +37,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
\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.
This file contains configuration parameters for the \fBtelemprobd\fP(1) and \fBtelempostd\fP(1) telemetry service daemons. The daemon reads this file at startup if it exists.
.SH SYNTAX
.sp
The configuration file contains \fBkey=value\fP pairs, formatted as plain
@@ -54,7 +53,7 @@ Server URL including protocol designator.
.IP \(bu 2
\fBsocket_path=<path>\fP
.sp
Path to the socket that \fItelemd\fP will listen on.
Path to the socket that \fItelemprobd\fP will listen on.
.IP \(bu 2
\fBcainfo=<path>\fP
.sp
@@ -82,7 +81,7 @@ 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
Time in seconds for processing spool. Valid range: 120..300. Values
outside this range are clamped.
.IP \(bu 2
\fBrate_limit_enabled=<true|false>\fP
@@ -114,7 +113,9 @@ delivery over network. Valid stategies: \fBspool\fP, \fBdrop\fP\&.
.SH SEE ALSO
.INDENT 0.0
.IP \(bu 2
\fBtelemd\fP(1)
\fBtelemprobd\fP(1)
.IP \(bu 2
\fBtelempostd\fP(1)
.IP \(bu 2
\fI\%https://github.com/clearlinux/telemetrics\-client\fP
.IP \(bu 2
+5 -6
View File
@@ -21,8 +21,7 @@ SYNOPSIS
DESCRIPTION
===========
This file contains configuration parameters for the ``telemd``\(1) telemetry
service daemon. The daemon reads this file at startup if it exists.
This file contains configuration parameters for the ``telemprobd``\(1) and ``telempostd``\(1) telemetry service daemons. The daemon reads this file at startup if it exists.
SYNTAX
@@ -43,7 +42,7 @@ OPTIONS
- ``socket_path=<path>``
Path to the socket that `telemd` will listen on.
Path to the socket that `telemprobd` will listen on.
- ``cainfo=<path>``
@@ -71,7 +70,7 @@ OPTIONS
- ``spool_process_time=<seconds>``
Time in seconds for processing spool. Valid range: 120..3600. Values
Time in seconds for processing spool. Valid range: 120..300. Values
outside this range are clamped.
- ``rate_limit_enabled=<true|false>``
@@ -104,7 +103,7 @@ OPTIONS
SEE ALSO
========
* ``telemd``\(1)
* ``telemprobd``\(1)
* ``telempostd``\(1)
* https://github.com/clearlinux/telemetrics-client
* https://clearlinux.org/documentation/
+5 -5
View File
@@ -44,11 +44,11 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.sp
\fBvoid tm_free_record(struct telem_ref *t_ref)\fP
.sp
\fBvoid tm_set_config_file(char *c_file)\fP
\fBint tm_set_config_file(const 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.
telemetry data to the \fBtelemprobd\fP(1) service.
.sp
The function \fBtm_create_record()\fP initializes a telemetry record and
sets the severity and classification of that record, as well as the
@@ -67,12 +67,12 @@ 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.
if an error occurred. The function \fBtm_free_record()\fP does not return
any value.
.SH SEE ALSO
.INDENT 0.0
.IP \(bu 2
\fBtelemd\fP(1)
\fBtelemprobd\fP(1)
.IP \(bu 2
\fI\%https://github.com/clearlinux/telemetrics\-client\fP
.IP \(bu 2
+7 -8
View File
@@ -14,9 +14,9 @@ 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)``
@@ -25,14 +25,14 @@ SYNOPSIS
``void tm_free_record(struct telem_ref *t_ref)``
``void tm_set_config_file(char *c_file)``
``int tm_set_config_file(const char *c_file)``
DESCRIPTION
===========
The functions in the telemetry library facilitate the delivery of
telemetry data to the ``telemd``\(1) service.
telemetry data to the ``telemprobd``\(1) service.
The function ``tm_create_record()`` initializes a telemetry record and
sets the severity and classification of that record, as well as the
@@ -54,14 +54,13 @@ 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.
if an error occurred. The function ``tm_free_record()`` does not return
any value.
SEE ALSO
========
* ``telemd``\(1)
* ``telemprobd``\(1)
* https://github.com/clearlinux/telemetrics-client
* https://clearlinux.org/documentation/
+97
View File
@@ -0,0 +1,97 @@
.\" Man page generated from reStructuredText.
.
.TH TELEMPOSTD 1 "" "" ""
.SH NAME
telempostd \- Telemetry client service
.
.nr rst2man-indent-level 0
.
.de1 rstReportMargin
\\$1 \\n[an-margin]
level \\n[rst2man-indent-level]
level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
-
\\n[rst2man-indent0]
\\n[rst2man-indent1]
\\n[rst2man-indent2]
..
.de1 INDENT
.\" .rstReportMargin pre:
. RS \\$1
. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
. nr rst2man-indent-level +1
.\" .rstReportMargin post:
..
.de UNINDENT
. RE
.\" indent \\n[an-margin]
.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
.nr rst2man-indent-level -1
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.SH SYNOPSIS
.sp
\fBtelempostd\fP <flags>
.sp
\fB/etc/telemetrics/telemetrics.conf\fP
.sp
\fB/usr/share/defaults/telemetrics/telemetrics.conf\fP
.sp
\fB/etc/telemetrics/opt\-in\-static\-machine\-id\fP
.SH DESCRIPTION
.sp
The \fBtelempostd\fP program delivers locally generated telemetry records to a remote
telemetry service. Telemetry data can be in any format, and is relayed as\-is.
.SH OPTIONS
.INDENT 0.0
.INDENT 3.5
.INDENT 0.0
.IP \(bu 2
\fB\-f\fP, \fB\-\-config_file\fP [<file>]:
Configuration file. This overrides the other parameters.
.IP \(bu 2
\fB\-h\fP, \fB\-\-help\fP:
Display this help message.
.IP \(bu 2
\fB\-V\fP, \fB\-\-version\fP:
Print the program version.
.UNINDENT
.UNINDENT
.UNINDENT
.SH FILES
.INDENT 0.0
.IP \(bu 2
\fB/usr/share/defaults/telemetrics/telemetrics.conf\fP
.INDENT 2.0
.INDENT 3.5
If no custom configuration file is found, \fBtelempostd\fP uses the
settings in this file.
.UNINDENT
.UNINDENT
.IP \(bu 2
\fB/etc/telemetrics/telemetrics.conf\fP
.INDENT 2.0
.INDENT 3.5
Custom configuration file that \fBtelempostd\fP reads. See \fBtelemetrics.conf\fP(5).
.UNINDENT
.UNINDENT
.UNINDENT
.SH EXIT STATUS
.sp
0 when no errors occurred. A non\-zero exit status indicates a failure occurred.
.SH SEE ALSO
.INDENT 0.0
.IP \(bu 2
\fBtelemetry\fP(3)
.IP \(bu 2
\fBtelemetrics.conf\fP(5)
.IP \(bu 2
\fI\%https://github.com/clearlinux/telemetrics\-client\fP
.IP \(bu 2
\fI\%https://clearlinux.org/documentation/\fP
.UNINDENT
.SH COPYRIGHT
(C) 2018 Intel Corporation, CC-BY-SA-3.0
.\" Generated by docutils manpage writer.
.
+71
View File
@@ -0,0 +1,71 @@
==========
telempostd
==========
------------------------
Telemetry client service
------------------------
:Copyright: \(C) 2018 Intel Corporation, CC-BY-SA-3.0
:Manual section: 1
SYNOPSIS
========
``telempostd`` \<flags\>
``/etc/telemetrics/telemetrics.conf``
``/usr/share/defaults/telemetrics/telemetrics.conf``
``/etc/telemetrics/opt-in-static-machine-id``
DESCRIPTION
===========
The ``telempostd`` program delivers locally generated telemetry records to a remote
telemetry service. Telemetry data can be in any format, and is relayed as-is.
OPTIONS
=======
* ``-f``, ``--config_file`` \[\<file\>\]:
Configuration file. This overrides the other parameters.
* ``-h``, ``--help``:
Display this help message.
* ``-V``, ``--version``:
Print the program version.
FILES
=====
* ``/usr/share/defaults/telemetrics/telemetrics.conf``
If no custom configuration file is found, ``telempostd`` uses the
settings in this file.
* ``/etc/telemetrics/telemetrics.conf``
Custom configuration file that ``telempostd`` reads. See ``telemetrics.conf``\(5).
EXIT STATUS
===========
0 when no errors occurred. A non-zero exit status indicates a failure occurred.
SEE ALSO
========
* ``telemetry``\(3)
* ``telemetrics.conf``\(5)
* https://github.com/clearlinux/telemetrics-client
* https://clearlinux.org/documentation/
+7 -7
View File
@@ -1,8 +1,8 @@
.\" Man page generated from reStructuredText.
.
.TH TELEMD 1 "" "" ""
.TH TELEMPROBD 1 "" "" ""
.SH NAME
telemd \- Telemetry client service
telemprobd \- Telemetry probes service
.
.nr rst2man-indent-level 0
.
@@ -32,7 +32,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
..
.SH SYNOPSIS
.sp
\fBtelemd\fP <flags>
\fBtelemprobd\fP <flags>
.sp
\fB/etc/telemetrics/telemetrics.conf\fP
.sp
@@ -41,8 +41,8 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
\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.
The \fBtelemprobd\fP program handles communication between telemetry client and telemetry
probes.
.SH OPTIONS
.INDENT 0.0
.INDENT 3.5
@@ -65,7 +65,7 @@ Print the program version.
\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
If no custom configuration file is found, \fBtelemprobd\fP uses the
settings in this file.
.UNINDENT
.UNINDENT
@@ -73,7 +73,7 @@ settings in this file.
\fB/etc/telemetrics/telemetrics.conf\fP
.INDENT 2.0
.INDENT 3.5
Custom configuration file that \fBtelemd\fP reads. See \fBtelemetrics.conf\fP(5).
Custom configuration file that \fBtelemprobd\fP reads. See \fBtelemetrics.conf\fP(5).
.UNINDENT
.UNINDENT
.IP \(bu 2
@@ -1,9 +1,9 @@
======
telemd
======
==========
telemprobd
==========
------------------------
Telemetry client service
Telemetry probes service
------------------------
:Copyright: \(C) 2017 Intel Corporation, CC-BY-SA-3.0
@@ -13,7 +13,7 @@ Telemetry client service
SYNOPSIS
========
``telemd`` \<flags\>
``telemprobd`` \<flags\>
``/etc/telemetrics/telemetrics.conf``
@@ -25,8 +25,8 @@ SYNOPSIS
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.
The ``telemprobd`` program handles communication between telemetry client and telemetry
probes.
OPTIONS
@@ -47,12 +47,12 @@ FILES
* ``/usr/share/defaults/telemetrics/telemetrics.conf``
If no custom configuration file is found, ``telemd`` uses the
If no custom configuration file is found, ``telemprobd`` uses the
settings in this file.
* ``/etc/telemetrics/telemetrics.conf``
Custom configuration file that ``telemd`` reads. See ``telemetrics.conf``\(5).
Custom configuration file that ``telemprobd`` reads. See ``telemetrics.conf``\(5).
* ``/etc/telemetrics/opt-in-static-machine-id``
+12 -9
View File
@@ -2,17 +2,19 @@
declare -a SPECIAL_UNITS=(
hprobe.timer
telemd.socket
telemd.path
telemprobd.socket
telempostd.path
klogscanner.service
journal-probe-tail.service
python-probe.path
)
declare -a SERVICES=(
hprobe.service
journal-probe.service
oops-probe.service
pstore-probe.service
klogscanner.service
telemd.service
telemprobd.service
telempostd.service
journal-probe.service
)
SCRIPT="$0"
@@ -69,7 +71,8 @@ telem_start() {
telem_is_active() {
# check only activation units
systemctl is-active telemd.socket
echo "telemprobd :" $(systemctl is-active telemprobd.socket)
echo "telempostd :" $(systemctl is-active telempostd.path)
}
telem_opt_out() {
@@ -103,10 +106,10 @@ usage() {
printf "$format" "stop" "Stops all running telemetry services"
printf "$format" "start" "Starts all telemetry services"
printf "$format" "restart" "Restarts all telemetry services"
printf "$format" "is-active" "Checks if telemd is active"
printf "$format" "is-active" "Checks if telemprobd and telempostd are active"
printf "$format" "opt-in" "Opts in to telemetry, and starts telemetry services"
printf "$format" "opt-out" "Opts out of telemetry, and stops telemetry services"
printf "$format" "journal" "Prints telemd journal contents. Use -h argument with"
printf "$format" "journal" "Prints telemtry journal contents. Use -h argument with"
printf "$format" "" "command for more options"
printf "\n"
exit 2
+5 -8
View File
@@ -78,7 +78,7 @@ static const uint32_t RECORD_FORMAT_VERSION = 4;
#define TM_OPT_OUT_FILE "/etc/telemetrics/opt-out"
/* Currently max supported payload size is 8kb */
#define MAX_PAYLOAD_SIZE 8192
#define MAX_PAYLOAD_LENGTH 8192
/* Maximum size for a category string is 122 bytes */
#define MAX_CLASS_LENGTH 122
@@ -86,12 +86,12 @@ static const uint32_t RECORD_FORMAT_VERSION = 4;
/* Maximum size for a sub category 40 bytes */
#define MAX_SUBCAT_LENGTH 40
/* Max Payload size is 8 kibabytes (8192) */
#define MAX_PAYLOAD_LENGTH 8192
/* Exit daemon after idle for this duration in seconds */
#define TM_DAEMON_EXIT_TIME (5 * 60)
/* SPOOL DEFINES */
/* Spooling should run every TM_SPOOL_RUN_MAX atleast */
#define TM_SPOOL_RUN_MAX (60 /*min*/ * 60 /*sec*/)
/* Spooling should run at least every TM_SPOOL_RUN_MAX */
#define TM_SPOOL_RUN_MAX TM_DAEMON_EXIT_TIME
/* Spooling should not run more often than TM_SPOOL_RUN_MIN */
#define TM_SPOOL_RUN_MIN (2 /*min*/ * 60 /*sec*/)
@@ -102,9 +102,6 @@ static const uint32_t RECORD_FORMAT_VERSION = 4;
/* Maximum records that can be processed in a single spool run loop*/
#define TM_SPOOL_MAX_PROCESS_RECORDS 60
/* Time to check to exit from the daemon */
#define TM_DAEMON_EXIT_TIME (2 /*h*/ * 60 /*m*/ * 60 /*sec*/)
/* Very simple structure. Array of header strings and a payload. Calling
* program is reponsible for passing in the payload as a simple string.
*/
+17 -9
View File
@@ -52,7 +52,7 @@ const char *config_key_bool[] = { NULL, "rate_limit_enabled", "daemon_recycling_
static struct configuration config = { { 0 }, { 0 }, { 0 }, false, NULL };
int validate_config_file(char *f)
static int validate_config_file(const char *f)
{
struct stat sbuf;
@@ -60,26 +60,34 @@ int validate_config_file(char *f)
return -EINVAL;
}
/* check if the file exists and is a regular file */
if (stat(f, &sbuf) != 0) {
return -errno;
}
if (!S_ISREG(sbuf.st_mode)) {
return -EINVAL;
}
return 0;
}
void set_config_file(char *filename)
int set_config_file(const char *filename)
{
int ret;
/* TODO: This should return a status, but that will require coordinating
* an API change. For now, config-entries referencing a null config
* file will fail, so the end result still works. */
if (validate_config_file(filename) == 0) {
config_file = strdup(filename);
cmd_line_cfg = true;
if ((ret = validate_config_file(filename)) == 0) {
char *filename1 = strdup(filename);
if (filename1 == NULL) {
ret = -errno;
} else {
config_file = filename1;
cmd_line_cfg = true;
}
}
return ret;
}
bool read_config_from_file(char *config_file, struct configuration *config)
+1 -1
View File
@@ -63,7 +63,7 @@ typedef struct configuration {
} configuration;
/* Sets the configuration file to be used later */
void set_config_file(char *filename);
int set_config_file(const char *filename);
/* Parses the ini format config file */
bool read_config_from_file(char *filename, struct configuration *config);
+2 -2
View File
@@ -1,7 +1,7 @@
[Unit]
Description=Telemetrics BERT Probe
Requires=telemd.socket
After=telemd.socket
Requires=telemprobd.socket
After=telemprobd.socket
ConditionPathExists=!/etc/telemetrics/opt-out
ConditionPathExists=/sys/firmware/acpi/tables/data/BERT
+1 -1
View File
@@ -16,7 +16,7 @@ spool_dir=/tmp/spool
spool_max_size=1024
#time in seconds for processing spool
spool_process_time=900
spool_process_time=180
#rate limiting record burst limit
record_burst_limit=100
+3 -3
View File
@@ -1,11 +1,11 @@
[Unit]
Description=Telemetrics Heartbeat Probe
Requires=telemd.socket
After=telemd.socket
Requires=telemprobd.socket
After=telemprobd.socket
ConditionPathExists=!/etc/telemetrics/opt-out
[Service]
ExecStart=@bindir@/hprobe -H
ExecStart=@bindir@/hprobe -Hlu
User=telemetry
[Install]
+12
View File
@@ -0,0 +1,12 @@
[Unit]
Description=Telemetrics Systemd Journal Probe
Requires=telemprobd.socket
After=telemprobd.socket
ConditionPathExists=!/etc/telemetrics/opt-out
[Service]
ExecStart=@bindir@/journalprobe -t
User=telemetry
[Install]
WantedBy=multi-user.target
+2 -2
View File
@@ -1,7 +1,7 @@
[Unit]
Description=Telemetrics Systemd Journal Probe
Requires=telemd.socket
After=telemd.socket
Requires=telemprobd.socket
After=telemprobd.socket
ConditionPathExists=!/etc/telemetrics/opt-out
[Service]
+2 -2
View File
@@ -1,7 +1,7 @@
[Unit]
Description=Telemetrics Kernel Log Scanner
Requires=telemd.socket
After=telemd.socket oops-probe.service
Requires=telemprobd.socket
After=telemprobd.socket
[Service]
ExecStart=@bindir@/klogscanner
+38 -20
View File
@@ -16,15 +16,18 @@ EXTRA_DIST += \
%D%/hprobe.timer \
%D%/bert-probe.service.in \
%D%/journal-probe.service.in \
%D%/oops-probe.service.in \
%D%/journal-probe-tail.service.in \
%D%/python-probe.service.in \
%D%/python-probe.path.in \
%D%/pstore-probe.service.in \
%D%/klogscanner.service.in \
%D%/pstore-clean.service.in \
%D%/libtelemetry.pc.in \
%D%/telemd.path.in \
%D%/telemd.service.in \
%D%/telemd.socket.in \
%D%/telemd-update-trigger.service.in \
%D%/telempostd.service.in \
%D%/telempostd.path.in \
%D%/telemprobd.service.in \
%D%/telemprobd.socket.in \
%D%/telemprobd-update-trigger.service.in \
%D%/telemetrics-dirs.conf.in \
%D%/telemetrics.conf.in
@@ -52,14 +55,17 @@ systemdunit_DATA = \
%D%/hprobe.timer \
%D%/bert-probe.service \
%D%/journal-probe.service \
%D%/oops-probe.service \
%D%/journal-probe-tail.service \
%D%/python-probe.service \
%D%/python-probe.path \
%D%/pstore-probe.service \
%D%/klogscanner.service \
%D%/pstore-clean.service \
%D%/telemd.service \
%D%/telemd.socket \
%D%/telemd-update-trigger.service \
%D%/telemd.path
%D%/telemprobd.service \
%D%/telemprobd.socket \
%D%/telemprobd-update-trigger.service \
%D%/telempostd.service \
%D%/telempostd.path
%D%/hprobe.service: %D%/hprobe.service.in
$(pathfix) < $< > $@
@@ -70,7 +76,7 @@ systemdunit_DATA = \
%D%/journal-probe.service: %D%/journal-probe.service.in
$(pathfix) < $< > $@
%D%/oops-probe.service: %D%/oops-probe.service.in
%D%/journal-probe-tail.service: %D%/journal-probe-tail.service.in
$(pathfix) < $< > $@
%D%/pstore-probe.service: %D%/pstore-probe.service.in
@@ -82,16 +88,25 @@ systemdunit_DATA = \
%D%/pstore-clean.service: %D%/pstore-clean.service.in
$(pathfix) < $< > $@
%D%/telemd.service: %D%/telemd.service.in
%D%/telempostd.path: %D%/telempostd.path.in
$(pathfix) < $< > $@
%D%/telemd.socket: %D%/telemd.socket.in
%D%/telempostd.service: %D%/telempostd.service.in
$(pathfix) < $< > $@
%D%/telemd.path: %D%/telemd.path.in
%D%/python-probe.service: %D%/python-probe.service.in
$(pathfix) < $< > $@
%D%/telemd-update-trigger.service: %D%/telemd-update-trigger.service.in
%D%/python-probe.path: %D%/python-probe.path.in
$(pathfix) < $< > $@
%D%/telemprobd.service: %D%/telemprobd.service.in
$(pathfix) < $< > $@
%D%/telemprobd.socket: %D%/telemprobd.socket.in
$(pathfix) < $< > $@
%D%/telemprobd-update-trigger.service: %D%/telemprobd-update-trigger.service.in
$(pathfix) < $< > $@
sysctldir = @SYSTEMD_SYSCTLDIR@
@@ -104,16 +119,19 @@ systemconfdir = @SYSTEMD_SYSTEMCONFDIR@
systemconf_DATA = %D%/40-core-ulimit.conf
clean-local:
-rm -f %D%/telemd.service \
%D%/telemd.socket \
%D%/telemd.path \
%D%/telemd-update-trigger.service \
-rm -f %D%/telemprobd.service \
%D%/telemprobd.socket \
%D%/telempostd.service \
%D%/telempostd.path \
%D%/python-probe.service \
%D%/python-probe.path \
%D%/telemprobd-update-trigger.service \
%D%/telemetrics.conf \
%D%/telemetrics-dirs.conf \
%D%/libtelemetry.pc \
%D%/40-crash-probe.conf \
%D%/journal-probe.service \
%D%/oops-probe.service \
%D%/journal-probe-tail.service \
%D%/pstore-probe.service \
%D%/klogscanner.service \
%D%/pstore-clean.service \
-11
View File
@@ -1,11 +0,0 @@
[Unit]
Description=Telemetrics Kernel Oops Probe
Requires=telemd.socket
After=telemd.socket
[Service]
ExecStart=@bindir@/oopsprobe
User=telemetry
[Install]
WantedBy=multi-user.target
+2 -2
View File
@@ -1,7 +1,7 @@
[Unit]
Description=Telemetrics Pstore Probe
Requires=telemd.socket
After=telemd.socket
Requires=telemprobd.socket
After=telemprobd.socket
[Service]
ExecStart=@bindir@/pstoreprobe
+9
View File
@@ -0,0 +1,9 @@
[Unit]
Description=Python Probe Daemon Staging
ConditionPathExists=!/etc/telemetrics/opt-out
[Path]
DirectoryNotEmpty=/var/lib/telemetry/python
[Install]
WantedBy=multi-user.target
+13
View File
@@ -0,0 +1,13 @@
[Unit]
Description=Telemetrics Python Probe
Requires=telemprobd.socket
After=telemprobd.socket
ConditionPathExists=!/etc/telemetrics/opt-out
[Service]
User=telemetry
ExecStart=@bindir@/pythonprobe
[Install]
WantedBy=multi-user.target
+2 -1
View File
@@ -3,5 +3,6 @@ d @localstatedir@/spool/telemetry 0750 telemetry telemetry -
d @localstatedir@/log/telemetry 0750 telemetry telemetry -
d @localstatedir@/log/telemetry/records 0750 telemetry telemetry -
d @localstatedir@/cache/telemetry 0750 telemetry telemetry -
d @localstatedir@/cache/telemetry/oops 0750 telemetry telemetry -
d @localstatedir@/cache/telemetry/pstore 0750 telemetry telemetry -
d /var/lib/telemetry/python 01777 telemetry telemetry -
d /var/tmp/telemetry 01777 telemetry telemetry -
+2 -2
View File
@@ -23,8 +23,8 @@ spool_dir=@localstatedir@/spool/telemetry
spool_max_size=5120
# time in seconds for processing spool
# Valid range: 120..3600. Values outside this range are clamped.
spool_process_time=900
# Valid range: 120..300. Values outside this range are clamped.
spool_process_time=120
# rate limit enabled - if this is set to false then all rate-limiting disabled.
# It is possible to disable each rate-limit individually below.
@@ -1,5 +1,5 @@
[Unit]
Description=Telemetry Daemon Spool
Description=Telemetry Post Daemon staging
ConditionPathExists=!/etc/telemetrics/opt-out
[Path]
+12
View File
@@ -0,0 +1,12 @@
[Unit]
Description=Telemetrics Post Daemon
ConditionPathExists=!/etc/telemetrics/opt-out
[Service]
ExecStart=@bindir@/telempostd
WorkingDirectory=@localstatedir@/spool/telemetry
User=telemetry
[Install]
Also=telempostd.path
WantedBy=multi-user.target
@@ -4,4 +4,4 @@ BindsTo=update-triggers.target
[Service]
Type=oneshot
ExecStart=@systemctldir@/systemctl try-restart telemd.service
ExecStart=@systemctldir@/systemctl try-restart telemprobd.service
@@ -3,10 +3,10 @@ Description=Telemetrics Daemon
ConditionPathExists=!/etc/telemetrics/opt-out
[Service]
ExecStart=@bindir@/telemd
ExecStart=@bindir@/telemprobd
WorkingDirectory=@localstatedir@/spool/telemetry
User=telemetry
[Install]
Also=telemd.socket telemd.path
Also=telemprobd.socket
WantedBy=multi-user.target
+162
View File
@@ -0,0 +1,162 @@
/*
* This program is part of the Clear Linux Project
*
* Copyright 2018 Intel Corporation
*
* This program is free software; you can redistribute it and/or modify it under
* the terms and conditions of the GNU Lesser General Public License, as
* published by the Free Software Foundation; either version 2.1 of the License,
* or (at your option) any later version.
*
* This program is distributed in the hope it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*/
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <limits.h>
#include <stdbool.h>
#include <sys/stat.h>
#include "log.h"
#include "util.h"
#include "common.h"
#include "iorecord.h"
bool _fgets(char *s, int n, FILE *stream)
{
char *i;
if (!fgets(s, n, stream)) {
return false;
}
if ((i = strchr(s, '\n')) != NULL) {
*i = '\0';
}
return true;
}
void stage_record(char *filepath, char *headers[], char *body)
{
int tmpfd;
FILE *tmpfile = NULL;
// Use default path if not provided
if (filepath == NULL) {
telem_log(LOG_ERR, "filepath value must be provided, aborting\n");
exit(EXIT_FAILURE);
}
tmpfd = mkstemp(filepath);
if (!tmpfd) {
telem_perror("Error opening staging file");
close(tmpfd);
if (unlink(filepath)) {
telem_perror("Error deleting staging file");
}
goto clean_exit;
}
// open file
tmpfile = fdopen(tmpfd, "a");
if (!tmpfile) {
telem_perror("Error opening temp stage file");
close(tmpfd);
if (unlink(filepath)) {
telem_perror("Error deleting temp stage file");
}
goto clean_exit;
}
// write headers
for (int i = 0; i < NUM_HEADERS; i++) {
fprintf(tmpfile, "%s\n", headers[i]);
}
//write body
fprintf(tmpfile, "%s\n", body);
fflush(tmpfile);
fclose(tmpfile);
clean_exit:
return;
}
bool read_record(char *fullpath, char *headers[], char **body)
{
int i, ret = 0;
bool result = false;
FILE *fp = NULL;
long offset;
char line[LINE_MAX] = { 0 };
struct stat buf;
long size;
ret = stat(fullpath, &buf);
if (ret == -1) {
telem_log(LOG_ERR, "Unable to stat record %s in staging\n", fullpath);
return false;
}
size = buf.st_size;
fp = fopen(fullpath, "r");
if (fp == NULL) {
telem_log(LOG_ERR, "Unable to open file %s in staging\n", fullpath);
return false;
}
for (i = 0; i < NUM_HEADERS; i++) {
const char *header_name = get_header_name(i);
if (!_fgets(line, LINE_MAX, fp)) {
telem_log(LOG_ERR, "Error while parsing staged record\n");
fclose(fp);
return false;
}
//Get rid of trailing newline
result = get_header(line, header_name, &headers[i]);
if (!result) {
telem_log(LOG_ERR, "read_record: Incorrect"
" headers in record\n");
goto read_error;
}
}
offset = ftell(fp);
if (offset == -1) {
telem_perror("Failed to get staged file offset of record payload");
result = false;
goto read_error;
}
size = size - offset + 1;
*body = malloc((size_t)size);
if (*body == NULL) {
telem_log(LOG_ERR, "Could not allocate memory for payload from staged file\n");
result = false;
goto read_error;
}
memset(*body, 0, (size_t)size);
//read rest of file
size_t newlen = fread(*body, sizeof(char), (size_t)size, fp);
if (newlen == 0) {
telem_perror("Error reading staged file");
result = false;
goto read_error;
} else {
result = true;
}
read_error:
if (fp) {
fclose(fp);
}
return result;
}
+38
View File
@@ -0,0 +1,38 @@
/*
* This program is part of the Clear Linux Project
*
* Copyright 2018 Intel Corporation
*
* This program is free software; you can redistribute it and/or modify it under
* the terms and conditions of the GNU Lesser General Public License, as
* published by the Free Software Foundation; either version 2.1 of the License,
* or (at your option) any later version.
*
* This program is distributed in the hope it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*/
#include <stdbool.h>
/**
* Save a telemetry record to disk
*
* @param path pointer to a directory to save record
* @param headers pointer to array of headers and values
* @param body record message content
*
*/
void stage_record(char *path, char *headers[], char *body);
/**
* Reads a telemetry record
*
* @param fullpath pointer to full path file name
* @param headers pointer to array of headers and values
* @param body record message content
*
* @return true if successful otherwise false
*/
bool read_record(char *fullpath, char *headers[], char **body);
+61 -20
View File
@@ -1,39 +1,80 @@
bin_PROGRAMS = %D%/telemd
bin_PROGRAMS = \
%D%/telemprobd \
%D%/telempostd
%C%_telemd_SOURCES = \
%D%/main.c \
%C%_telemprobd_SOURCES = \
%D%/probe.c \
%D%/telemdaemon.c \
%D%/telemdaemon.h \
%D%/spool.c \
%D%/spool.h \
%D%/iorecord.c \
%D%/journal/journal.c \
%D%/journal/journal.h \
%D%/retention.c \
%D%/retention.h
%D%/journal/journal.h
%C%_telemd_LDADD = $(CURL_LIBS) \
%D%/libtelem-shared.la
%C%_telemprobd_LDADD = $(CURL_LIBS) \
%D%/libtelem-shared.la \
%D%/libtelemetry.la
%C%_telemd_CFLAGS = \
%C%_telemprobd_CFLAGS = \
$(AM_CFLAGS)
%C%_telemd_LDFLAGS = \
%C%_telemprobd_LDFLAGS = \
$(AM_LDFLAGS) \
-pie
if HAVE_SYSTEMD_DAEMON
%C%_telemd_CFLAGS += \
$(SYSTEMD_DAEMON_CFLAGS)
%C%_telemd_LDADD += \
$(SYSTEMD_DAEMON_LIBS)
%C%_telemprobd_CFLAGS += \
$(SYSTEMD_DAEMON_CFLAGS)
%C%_telemprobd_LDADD += \
$(SYSTEMD_DAEMON_LIBS)
endif
if LOG_SYSTEMD
if HAVE_SYSTEMD_JOURNAL
%C%_telemd_CFLAGS += \
$(SYSTEMD_JOURNAL_CFLAGS)
%C%_telemd_LDADD += \
$(SYSTEMD_JOURNAL_LIBS)
%C%_telemprobd_CFLAGS += \
$(SYSTEMD_JOURNAL_CFLAGS)
%C%_telemprobd_LDADD += \
$(SYSTEMD_JOURNAL_LIBS)
endif
endif
# Telemetry post daemon
%C%_telempostd_SOURCES = \
%D%/post.c \
%D%/telempostdaemon.c \
%D%/telempostdaemon.h \
%D%/journal/journal.c \
%D%/journal/journal.h \
%D%/spool.h \
%D%/spool.c \
%D%/retention.h \
%D%/retention.c \
%D%/iorecord.c \
%D%/iorecord.h
%C%_telempostd_LDADD = $(CURL_LIBS) \
%D%/libtelem-shared.la \
%D%/libtelemetry.la
%C%_telempostd_CFLAGS = \
$(AM_CFLAGS)
%C%_telempostd_LDFLAGS = \
$(AM_LDFLAGS) \
-pie
if HAVE_SYSTEMD_DAEMON
%C%_telempostd_CFLAGS += \
$(SYSTEMD_DAEMON_CFLAGS)
%C%_telempostd_LDADD += \
$(SYSTEMD_DAEMON_LIBS)
endif
if LOG_SYSTEMD
if HAVE_SYSTEMD_JOURNAL
%C%_telempostd_CFLAGS += \
$(SYSTEMD_JOURNAL_CFLAGS)
%C%_telempostd_LDADD += \
$(SYSTEMD_JOURNAL_LIBS)
endif
endif
+91
View File
@@ -0,0 +1,91 @@
/*
* This program is part of the Clear Linux Project
*
* Copyright 2018 Intel Corporation
*
* This program is free software; you can redistribute it and/or modify it under
* the terms and conditions of the GNU Lesser General Public License, as
* published by the Free Software Foundation; either version 2.1 of the License,
* or (at your option) any later version.
*
* This program is distributed in the hope it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*/
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <unistd.h>
#include <stdbool.h>
#include <sys/stat.h>
#include "telemetry.h"
#include "log.h"
#include "spool.h"
#include "configuration.h"
#include "telempostdaemon.h"
bool (*post_record_ptr)(char *[], char *) = post_record_http;
void print_usage(char *prog)
{
printf("%s: Usage\n", prog);
printf(" -f, --config_file Configuration file. This overides the other parameters\n");
printf(" -h, --help Display this help message\n");
printf(" -V, --version Print the program version\n");
}
int main(int argc, char **argv)
{
int c;
int opt_index = 0;
TelemPostDaemon daemon;
struct option opts[] = {
{ "config_file", 1, NULL, 'f' },
{ "help", 0, NULL, 'h' },
{ "version", 0, NULL, 'V' },
{ NULL, 0, NULL, 0 }
};
while ((c = getopt_long(argc, argv, "f:hV", opts, &opt_index)) != -1) {
switch (c) {
case 'f':
if (tm_set_config_file(optarg) != 0) {
telem_log(LOG_ERR, "Configuration file"
" path not valid\n");
exit(EXIT_FAILURE);
}
break;
case 'V':
printf(PACKAGE_VERSION "\n");
exit(EXIT_SUCCESS);
case 'h':
print_usage(argv[0]);
exit(EXIT_SUCCESS);
case '?':
print_usage(argv[0]);
exit(EXIT_FAILURE);
}
}
initialize_daemon(&daemon);
daemon.current_spool_size = get_spool_dir_size();
/* When path activated this will process
* the activating message or previously
* spooled data */
staging_records_loop(&daemon);
/* This function is blocking, it will
* block until a signal is received */
run_daemon(&daemon);
close_daemon(&daemon);
return 0;
}
+12 -58
View File
@@ -31,6 +31,7 @@
#include <signal.h>
#include <malloc.h>
#include "telemetry.h"
#include "config.h"
#include "common.h"
#ifdef HAVE_SYSTEMD_SD_DAEMON_H
@@ -49,15 +50,12 @@
#include "log.h"
#include "telemdaemon.h"
#include "configuration.h"
#include "retention.h"
#include "spool.h"
/*
* Using a function pointer for unit testing to isolate the call to actual post function.
* The call to post the record to the server is stubbed out in the unit tests
* using pointer to a fake function.
*/
bool (*post_record_ptr)(char *[], char *, bool) = post_record_http;
void print_usage(char *prog)
{
@@ -77,9 +75,7 @@ int main(int argc, char **argv)
client *cl = NULL;
client *current_client = NULL;
int c;
char *config_file = NULL;
int opt_index = 0;
nfds_t initial_nfds = 0;
sigset_t mask;
//bool interrupted = false;
@@ -96,18 +92,12 @@ int main(int argc, char **argv)
while ((c = getopt_long(argc, argv, "f:hV", opts, &opt_index)) != -1) {
switch (c) {
case 'f':
config_file = optarg;
struct stat buf;
ret = stat(config_file, &buf);
/* check if the file exists and is a regular file */
if (ret == -1 || !S_ISREG(buf.st_mode)) {
telem_log(LOG_ERR, "Configuration file"
case 'f':
if (tm_set_config_file(optarg) != 0) {
telem_log(LOG_ERR, "Configuration file"
" path not valid\n");
exit(EXIT_FAILURE);
exit(EXIT_FAILURE);
}
set_config_file(config_file);
break;
case 'h':
print_usage(argv[0]);
@@ -123,11 +113,6 @@ int main(int argc, char **argv)
}
initialize_daemon(&daemon);
/* Register record retention delete action as a callback to prune entry */
if (daemon.record_journal) {
daemon.record_journal->prune_entry_callback = &delete_record_by_id;
}
sigemptyset(&mask);
if (sigaddset(&mask, SIGHUP) != 0) {
@@ -227,19 +212,9 @@ int main(int argc, char **argv)
telem_log(LOG_INFO, "Listening on socket...\n");
/* Calculate the spool directory size */
if (is_spool_valid()) {
daemon.is_spool_valid = true;
daemon.current_spool_size = get_spool_dir_size();
}
int spool_process_time = spool_process_time_config();
spool_records_loop(&(daemon.current_spool_size));
time_t last_spool_run_time = time(NULL);
time_t last_daemon_start_time = time(NULL);
bool daemon_recycling_enabled = daemon_recycling_enabled_config();
int spool_process_time = spool_process_time_config();
time_t last_record_received = time(NULL);
ret = update_machine_id();
if (ret == -1) {
@@ -251,18 +226,9 @@ int main(int argc, char **argv)
time_t last_refresh_time = time(NULL);
/* Save initial count for non connection fds */
initial_nfds = daemon.nfds;
/* Loop to accept clients */
while (1) {
malloc_trim(0);
if (initial_nfds == daemon.nfds) {
ret = prune_journal(daemon.record_journal, JOURNAL_TMPDIR);
if (ret != 0) {
telem_log(LOG_WARNING, "Unable to prune journal\n");
}
}
ret = poll(daemon.pollfds, daemon.nfds, spool_process_time * 1000);
if (ret == -1) {
telem_perror("Failed to poll daemon file descriptors");
@@ -292,12 +258,6 @@ int main(int argc, char **argv)
telem_log(LOG_INFO, "Received a SIGHUP signal\n");
/* reload configuration file */
reload_config();
if (is_spool_valid()) {
daemon.is_spool_valid = true;
//calc_spool_dir_size();
daemon.current_spool_size = get_spool_dir_size();
}
}
}
@@ -349,25 +309,20 @@ int main(int argc, char **argv)
}
assert(current_client);
handle_client(&daemon, i, current_client);
last_record_received = time(NULL);
}
}
} else {
time_t now = time(NULL);
/* If spool is empty and time to recycle the daemon has elapsed*/
if (daemon_recycling_enabled && (daemon.current_spool_size == 0) &&
difftime(now, last_daemon_start_time) >= TM_DAEMON_EXIT_TIME) {
/* time to recycle the daemon has elapsed*/
if (daemon_recycling_enabled &&
difftime(now, last_record_received) >= TM_DAEMON_EXIT_TIME) {
/* Exit */
telem_log(LOG_INFO, "Daemon exiting for recycling\n");
goto clean_exit;
}
}
time_t now = time(NULL);
if (difftime(now, last_spool_run_time) >= spool_process_time) {
spool_records_loop(&(daemon.current_spool_size));
last_spool_run_time = time(NULL);
}
if (difftime(now, last_refresh_time) >= TM_REFRESH_RATE) {
int ret = update_machine_id();
if (ret == -1) {
@@ -378,14 +333,13 @@ int main(int argc, char **argv)
}
clean_exit:
close_journal(daemon.record_journal);
/* Free memory before exiting */
while ((cl = LIST_FIRST(&(daemon.client_head))) != NULL) {
remove_client(&(daemon.client_head), cl);
}
free(daemon.pollfds);
free(daemon.machine_id_override);
if (LIST_EMPTY(&(daemon.client_head))) {
telem_log(LOG_INFO, "Client list cleared\n");
}
+3 -1
View File
@@ -14,11 +14,13 @@ The default probes provided along the telemetry client code are:
* journalprobe: a probe that monitors the systemd journal for log messages
from failed services.
* oopsprobe: a probe to collect 'oops messages' when the kernel detects a
* klogscanner: a probe to collect 'oops messages' when the kernel detects a
problem.
* pstoreprobe: probe to collect messages left on pstore filesystem.
* pythonprobe: a probe that monitors Python generated traceback files.
* telem-record-gen: this is a "general-purpose probe" for sending custom
records on-the-fly. This tool can be used stand alone or as a part of a
script to implement a probe.
+20 -45
View File
@@ -25,6 +25,7 @@
#include "nica/b64enc.h"
#include "config.h"
#include "log.h"
static const char bert_record_file[] = "/sys/firmware/acpi/tables/data/BERT";
static const char telem_record_class[] = "org.clearlinux/bert/debug";
@@ -39,35 +40,16 @@ void print_usage(char *prog)
printf(" -V, --version Print the program version\n");
}
static int allocate_payload_buffer(char **payload)
{
int ret = 0;
*payload = (char *)malloc(MAX_PAYLOAD_SIZE);
if (*payload == NULL) {
goto out;
}
*payload = memset(*payload, 0, MAX_PAYLOAD_SIZE);
ret = 1;
out:
return ret;
}
int main(int argc, char **argv)
{
struct telem_ref *tm_handle = NULL;
char *classification = (char *)telem_record_class;
char *payload = NULL;
char *payload;
int ret;
// Following vars are for arg parsing.
int c;
char *cfile = NULL;
int opt_index = 0;
int ret = 0;
struct option opts[] = {
{ "config_file", 1, NULL, 'f' },
{ "help", 0, NULL, 'h' },
@@ -75,22 +57,14 @@ int main(int argc, char **argv)
{ NULL, 0, NULL, 0 }
};
while ((c = getopt_long(argc, argv, "f:hHV", opts, &opt_index)) != -1) {
while ((c = getopt_long(argc, argv, "f:hV", opts, &opt_index)) != -1) {
switch (c) {
case 'f':
if (asprintf(&cfile, "%s", (char *)optarg) < 0) {
exit(EXIT_FAILURE);
if (tm_set_config_file(optarg) != 0) {
telem_log(LOG_ERR, "Configuration file"
" path not valid\n");
exit(EXIT_FAILURE);
}
struct stat buf;
ret = stat(cfile, &buf);
/* check if the file exists and is a regular file */
if (ret == -1 || !S_ISREG(buf.st_mode)) {
exit(EXIT_FAILURE);
}
tm_set_config_file(cfile);
break;
case 'h':
print_usage(argv[0]);
@@ -103,36 +77,37 @@ int main(int argc, char **argv)
}
}
if (!(ret = allocate_payload_buffer(&payload))) {
payload = calloc(sizeof(char), MAX_PAYLOAD_LENGTH);
if (!payload) {
printf("Unable to allocate more memory.\n");
goto fail;
return -ENOMEM;
}
if (!(ret = nc_b64enc_filename(bert_record_file, payload, MAX_PAYLOAD_SIZE))) {
if (!(ret = nc_b64enc_filename(bert_record_file, payload, MAX_PAYLOAD_LENGTH))) {
printf("Failed to read payload from: %s\n", bert_record_file);
goto fail;
ret = EXIT_FAILURE;
goto done;
}
if ((ret = tm_create_record(&tm_handle, severity, classification,
payload_version)) < 0) {
printf("Failed to create record: %s\n", strerror(-ret));
goto fail;
goto done;
}
if ((ret = tm_set_payload(tm_handle, payload)) < 0) {
printf("Failed to set record payload: %s\n", strerror(-ret));
goto fail;
goto done;
}
free(payload);
if ((ret = tm_send_record(tm_handle)) < 0) {
printf("Failed to send record to daemon: %s\n", strerror(-ret));
goto fail;
goto done;
}
fail:
free(cfile);
ret = EXIT_SUCCESS;
done:
free(payload);
tm_free_record(tm_handle);
tm_handle = NULL;
+10 -21
View File
@@ -69,7 +69,6 @@ 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 =
{
@@ -463,7 +462,6 @@ static bool is_banned_path(char *fullpath)
return false;
}
static char *config_file = NULL;
static char *core_file = NULL;
static char *proc_path = NULL;
static long int signal_num = -1;
@@ -490,7 +488,7 @@ static void print_help(void)
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(" -f, --config_file Specify a configuration file other than default\n");
printf(" -c, --core-file Path to core file to process\n");
printf(" -p, --process-name Name of process for crash report (required)\n");
printf(" -E, --process-path Absolute path of crashed process, with ! or / delimiters\n");
@@ -532,7 +530,11 @@ int main(int argc, char **argv)
printf(PACKAGE_VERSION "\n");
goto success;
case 'f':
config_file = strdup(optarg);
if (tm_set_config_file(optarg) != 0) {
telem_log(LOG_ERR, "Configuration file"
" path not valid\n");
exit(EXIT_FAILURE);
}
break;
case 'c':
core_file = strdup(optarg);
@@ -606,8 +608,6 @@ int main(int argc, char **argv)
backtrace = nc_string_dup("Crash from Clear package build\n");
keep_core = true;
if (!send_data(&backtrace, unknown_severity, clr_build_class)) {
goto fail;
}
@@ -619,8 +619,6 @@ int main(int argc, char **argv)
backtrace = nc_string_dup("Crash from third party\n");
keep_core = true;
if (!send_data(&backtrace, unknown_severity, unknown_class)) {
goto fail;
}
@@ -641,16 +639,12 @@ int main(int argc, char **argv)
nc_string_append_printf(header, "Signal: %ld\n", signal_num);
}
if (process_corefile(&backtrace) < 0) {
goto fail;
}
/* On Clear Linux OS, missing symbols may appear if automatic debuginfo
* downloads are still in flight. So if any missing symbols appear on
* the first run (indicated by the presence of "??? - ["), wait 10
* seconds and try again.
* seconds and try again. Also retry if errors occur in the first run.
*/
if (strstr(backtrace->str, "??? - [")) {
if ((process_corefile(&backtrace) < 0) || (strstr(backtrace->str, "??? - ["))) {
sleep(10);
if (prepare_corefile(&e_core, core_fd) < 0) {
@@ -677,11 +671,6 @@ success:
ret = EXIT_SUCCESS;
fail:
// 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);
@@ -711,8 +700,8 @@ fail:
}
// 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) {
// is specified.
if (!core_file) {
unlink(temp_core);
}
+131 -33
View File
@@ -1,7 +1,7 @@
/*
* This program is part of the Clear Linux Project
*
* Copyright 2015 Intel Corporation
* Copyright 2015-2019 Intel Corporation
*
* This program is free software; you can redistribute it and/or modify it under
* the terms and conditions of the GNU Lesser General Public License, as
@@ -20,63 +20,156 @@
#include <string.h>
#include <sys/stat.h>
#include <getopt.h>
#include <locale.h>
#include <sys/sysinfo.h>
#include <dirent.h>
#include "common.h"
#include "telemetry.h"
#include "config.h"
#include "log.h"
void print_usage(char *prog)
#define PAYLOAD_LOCALE 0x01
#define PAYLOAD_UPTIME 0x02
#define PAYLOAD_BUNDLES 0x04
static void print_usage(char *prog)
{
printf("%s: Usage\n", prog);
printf(" -f, --config_file Configuration file. This overides the other parameters\n");
printf(" -H, --heartbeat Create a \"heartbeat\" record instead\n");
printf(" -h, --help Display this help message\n");
printf(" -l, --locale Include locale in the record\n");
printf(" -u, --uptime Include uptime in the record\n");
printf(" -b, --bundles Include installed bundles in the record\n");
printf(" -V, --version Print the program version\n");
}
static long get_uptime(void)
{
struct sysinfo s_info;
if (sysinfo(&s_info) == 0) {
return s_info.uptime;
}
return 0;
}
static int nodots(const struct dirent *dp)
{
return (dp->d_name[0] != '.');
}
static char *create_payload(unsigned options)
{
int n, buflen;
char *ppos, *payload;
payload = calloc(sizeof(char), MAX_PAYLOAD_LENGTH);
if (payload == NULL) {
return NULL;
}
ppos = payload;
buflen = MAX_PAYLOAD_LENGTH;
n = snprintf(ppos, (size_t)buflen, "hello\n");
buflen -= n, ppos += n;
if (options & PAYLOAD_LOCALE) {
n = snprintf(ppos, (size_t)buflen, "LC_ALL: %s\n", setlocale(LC_ALL, NULL));
buflen -= n, ppos += n;
}
if (options & PAYLOAD_UPTIME) {
n = snprintf(ppos, (size_t)buflen, "uptime: %ld\n", get_uptime());
buflen -= n, ppos += n;
}
if (options & PAYLOAD_BUNDLES) {
int num_bundles, i;
struct dirent **entries;
num_bundles = scandir("/usr/share/clear/bundles", &entries, nodots, alphasort);
if (num_bundles < 0) {
telem_log(LOG_ERR, "scandir failed: %s\n", strerror(errno));
free(payload);
return NULL;
}
n = snprintf(ppos, (size_t)buflen, "\nBundles (%d):\n", num_bundles);
buflen -= n, ppos += n;
for (i = 0; i < num_bundles; i++) {
n = snprintf(ppos, (size_t)buflen, "%s\n", entries[i]->d_name);
/* Test if truncated output */
if (n >= buflen) {
int lastix = MAX_PAYLOAD_LENGTH-1;
payload[lastix] = '\0';
payload[lastix-1] = '.';
payload[lastix-2] = '.';
payload[lastix-3] = '.';
payload[lastix-4] = '\n';
break;
}
buflen -= n, ppos += n;
}
for (i= 0; i < num_bundles; i++) {
free(entries[i]);
}
free(entries);
}
return payload;
}
int main(int argc, char **argv)
{
uint32_t severity = 1;
uint32_t payload_version = 1;
char classification[30] = "org.clearlinux/hello/world";
struct telem_ref *tm_handle = NULL;
char *payload2;
char *payload;
char *str;
int ret;
unsigned payload_options = 0;
// Following vars are for arg parsing.
int c;
char *cfile = NULL;
int opt_index = 0;
int ret = 0;
struct option opts[] = {
{ "config_file", 1, NULL, 'f' },
{ "heartbeat", 0, NULL, 'H' },
{ "help", 0, NULL, 'h' },
{ "version", 0, NULL, 'V' },
{ "locale", 0, NULL, 'l' },
{ "uptime", 0, NULL, 'u' },
{ "bundles", 0, NULL, 'b' },
{ NULL, 0, NULL, 0 }
};
while ((c = getopt_long(argc, argv, "f:hHV", opts, &opt_index)) != -1) {
while ((c = getopt_long(argc, argv, "f:hHVlub", opts, &opt_index)) != -1) {
switch (c) {
case 'f':
if (asprintf(&cfile, "%s", (char *)optarg) < 0) {
exit(EXIT_FAILURE);
if (tm_set_config_file(optarg) != 0) {
telem_log(LOG_ERR, "Configuration file"
" path not valid\n");
exit(EXIT_FAILURE);
}
struct stat buf;
ret = stat(cfile, &buf);
/* check if the file exists and is a regular file */
if (ret == -1 || !S_ISREG(buf.st_mode)) {
exit(EXIT_FAILURE);
}
tm_set_config_file(cfile);
break;
case 'H':
str = "org.clearlinux/heartbeat/ping";
memcpy(classification, str, strlen(str));
break;
case 'l':
payload_options |= PAYLOAD_LOCALE;
break;
case 'u':
payload_options |= PAYLOAD_UPTIME;
break;
case 'b':
payload_options |= PAYLOAD_BUNDLES;
break;
case 'h':
print_usage(argv[0]);
exit(EXIT_SUCCESS);
@@ -88,34 +181,39 @@ int main(int argc, char **argv)
}
}
if (asprintf(&payload2, "hello\n") < 0) {
setlocale(LC_ALL, "");
payload = create_payload(payload_options);
if (payload == NULL) {
exit(EXIT_FAILURE);
}
if ((ret = tm_create_record(&tm_handle, severity, classification,
payload_version)) < 0) {
printf("Failed to create record: %s\n", strerror(-ret));
ret = 1;
goto fail;
telem_log(LOG_ERR, "Failed to create record: %s\n",
strerror(-ret));
ret = EXIT_FAILURE;
goto done;
}
if ((ret = tm_set_payload(tm_handle, payload2)) < 0) {
printf("Failed to set record payload: %s\n", strerror(-ret));
ret = 1;
goto fail;
if ((ret = tm_set_payload(tm_handle, payload)) < 0) {
telem_log(LOG_ERR, "Failed to set record payload: %s\n",
strerror(-ret));
ret = EXIT_FAILURE;
goto done;
}
free(payload2);
free(payload);
if ((ret = tm_send_record(tm_handle)) < 0) {
printf("Failed to send record to daemon: %s\n", strerror(-ret));
ret = 1;
goto fail;
telem_log(LOG_ERR, "Failed to send record to daemon: %s\n",
strerror(-ret));
ret = EXIT_FAILURE;
goto done;
} else {
printf("Successfully sent record to daemon.\n");
ret = 0;
ret = EXIT_SUCCESS;
}
fail:
done:
tm_free_record(tm_handle);
tm_handle = NULL;
+23 -16
View File
@@ -265,7 +265,7 @@ static bool add_filters(sd_journal *journal)
return true;
}
static bool process_journal(void)
static bool process_journal(bool process_existing)
{
int r;
@@ -279,8 +279,14 @@ static bool process_journal(void)
return false;
}
if (!process_existing_entries(journal)) {
return false;
if (process_existing) {
if (!process_existing_entries(journal)) {
return false;
}
} else {
if ((sd_journal_seek_tail(journal) < 0) || (sd_journal_previous(journal) < 0)) {
return false;
}
}
struct pollfd pfd;
@@ -312,12 +318,11 @@ static bool process_journal(void)
}
}
static char *config_file = NULL;
static const struct option prog_opts[] = {
{ "help", no_argument, 0, 'h' },
{ "config-file", required_argument, 0, 'f' },
{ "version", no_argument, 0, 'V' },
{ "tail", no_argument, 0, 't' },
{ 0, 0, 0, 0 }
};
@@ -330,22 +335,19 @@ static void print_help(void)
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(" -f, --config_file Specify a configuration file other than default\n");
printf(" -t, --tail Don't process entries already in the journal\n");
printf(" -V, --version Print the program version\n");
printf("\n");
}
static void free_strings(void)
{
free(config_file);
}
int main(int argc, char **argv)
{
int ret = EXIT_FAILURE;
int opt;
bool process_existing = true;
while ((opt = getopt_long(argc, argv, "hf:V", prog_opts, NULL)) != -1) {
while ((opt = getopt_long(argc, argv, "hf:Vt", prog_opts, NULL)) != -1) {
switch (opt) {
case 'h':
print_help();
@@ -354,20 +356,25 @@ int main(int argc, char **argv)
printf(PACKAGE_VERSION "\n");
goto success;
case 'f':
config_file = strdup(optarg);
if (tm_set_config_file(optarg) != 0) {
telem_log(LOG_ERR, "Configuration file"
" path not valid\n");
exit(EXIT_FAILURE);
}
break;
case 't':
process_existing = false;
break;
}
}
if (!process_journal()) {
if (!process_journal(process_existing)) {
goto fail;
}
success:
ret = EXIT_SUCCESS;
fail:
free_strings();
if (journal) {
sd_journal_close(journal);
}
+43 -34
View File
@@ -1,9 +1,24 @@
/*
* This program is part of the Clear Linux Project
*
* Copyright © 2015-2019 Intel Corporation
*
* This program is free software; you can redistribute it and/or modify it under
* the terms and conditions of the GNU Lesser General Public License, as
* published by the Free Software Foundation; either version 2.1 of the License,
* or (at your option) any later version.
*
* This program is distributed in the hope it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*/
#define _GNU_SOURCE
#include <stdlib.h>
#include <stdio.h>
#include <sys/klog.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <malloc.h>
@@ -22,54 +37,48 @@ int main(void)
int log_size = 0;
char *bufp = NULL;
size_t buflen = 0;
int bytes = 0;
oops_parser_init(write_oops_to_file);
oops_parser_init(klog_process_oops_msgs);
// Allocate space to write the oops file to
if (allocate_filespace() < 0) {
telem_log(LOG_ERR, "%s\n", strerror(errno));
return 1;
}
// Signal Handling to close and unlink empty files
if (signal (SIGINT, signal_handler) == SIG_ERR) {
// Signal Handling to terminate the probe.
if (signal (SIGINT, signal_handler_fail) == SIG_ERR) {
telem_log(LOG_ERR, "Error handling interrupt signal\n");
}
if (signal (SIGTERM, signal_handler) == SIG_ERR) {
if (signal (SIGTERM, signal_handler_success) == SIG_ERR) {
telem_log(LOG_ERR, "Error handling terminating signal\n");
}
// Gets the size of the kernel ring buffer
log_size = klogctl(SYSLOG_ACTION_SIZE_BUFFER, NULL, 0);
if (log_size < 0) {
telem_log(LOG_ERR, "Cannot read size of kernel ring buffer: %s\n", strerror(errno));
return 1;
}
buflen = (size_t)log_size;
if (buflen > MAX_BUF) {
buflen = MAX_BUF;
}
// Gets the contents of the kernel ring buffer
bufp = (char *)malloc(buflen);
while (1) {
// Gets the size of the kernel ring buffer
log_size = klogctl(SYSLOG_ACTION_SIZE_BUFFER, NULL, 0);
if (log_size < 0) {
telem_log(LOG_ERR, "Cannot read size of kernel ring buffer: %s\n", strerror(errno));
return 1;
}
buflen = (size_t)log_size;
if (buflen > MAX_BUF) {
buflen = MAX_BUF;
}
// Gets the contents of the kernel ring buffer
bufp = (char *)calloc(buflen, sizeof(char));
int bytes_read;
malloc_trim(0);
bytes = klogctl(SYSLOG_ACTION_READ, bufp, (int)buflen);
if (bytes < 0) {
memset(bufp, 0, buflen);
bytes_read = klogctl(SYSLOG_ACTION_READ, bufp, (int)buflen);
if (bytes_read < 0) {
telem_log(LOG_ERR, "Cannot read contents of kernel ring buffer: %s\n", strerror(errno));
return 1;
}
// Splits the buffer into separate lines with /0 terminating
split_buf_by_line(bufp, (size_t)bytes);
free(bufp);
klog_process_buffer(bufp, (size_t)bytes_read);
}
// Not reached
free(bufp);
return 0;
}
+95 -69
View File
@@ -1,7 +1,7 @@
/*
* This program is part of the Clear Linux Project
*
* Copyright 2015 Intel Corporation
* Copyright © 2015-2019 Intel Corporation
*
* This program is free software; you can redistribute it and/or modify it under
* the terms and conditions of the GNU Lesser General Public License, as
@@ -15,11 +15,10 @@
*/
#define _GNU_SOURCE
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/klog.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
@@ -27,20 +26,54 @@
#include "log.h"
#include "oops_parser.h"
#include "klog_scanner.h"
#include "telemetry.h"
#include "nica/nc-string.h"
#define MAX_OOPS_MSG_LENGTH 1024
#define FILE_PATH KERNELOOPSDIR "/kerneloopsXXXXXX"
static bool oops_processed = false;
static int fd;
char filename[] = FILE_PATH;
int last_line_length;
static uint32_t version = 1;
static bool send_data(char *backtrace, char *class, uint32_t severity)
{
struct telem_ref *handle = NULL;
int ret;
if ((ret = tm_create_record(&handle, severity, class, version)) < 0) {
telem_log(LOG_ERR, "Failed to create record: %s",
strerror(-ret));
return false;
}
if ((ret = tm_set_payload(handle, backtrace)) < 0) {
telem_log(LOG_ERR, "Failed to set payload: %s", strerror(-ret));
tm_free_record(handle);
return false;
}
if ((ret = tm_send_record(handle)) < 0) {
telem_log(LOG_ERR, "Failed to send record: %s", strerror(-ret));
tm_free_record(handle);
return false;
}
tm_free_record(handle);
return true;
}
int klog_process_buffer(char *bufp, int bytes)
{
// Splits the buffer into separate lines with /0 terminating
split_buf_by_line(bufp, (size_t)bytes);
return 0;
}
bool oops_written = false;
void split_buf_by_line(char *bufp, int bytes)
{
char *start;
char *eol;
size_t linelength = 0;
oops_processed = false;
while (bytes > 0) {
eol = memchr(bufp, '\n', (size_t)bytes);
@@ -57,87 +90,80 @@ void split_buf_by_line(char *bufp, int bytes)
if (bytes >= 0) {
start[linelength] = '\0';
parse_single_line(start, linelength);
if (oops_processed == true) {
#ifdef DEBUG
printf("[%s] oops_processed detected !\n", __func__);
#endif
break;
}
linelength = 0;
}
}
}
int allocate_filespace()
void klog_process_oops_msgs(struct oops_log_msg *msg)
{
int allocate_disk = 0;
strcpy(filename, FILE_PATH);
fd = mkstemp(filename);
if (fchmod(fd, 0644) == -1) {
telem_log(LOG_ERR, "Error while changing permissions for file %s: %s\n",
filename, strerror(errno));
}
if (fd == -1) {
telem_log(LOG_ERR, "fd error: %s\n", strerror(errno));
return -1;
}
// Allocates disk space to write kernel oops to
allocate_disk = fallocate(fd, 0, 0, MAX_OOPS_MSG_LENGTH);
if (allocate_disk != 0) {
telem_log(LOG_ERR, "Error allocating disk space: %s\n", strerror(errno));
return -1;
}
return 0;
}
void write_oops_to_file(struct oops_log_msg *msg)
{
ssize_t writer;
size_t linelength;
size_t linelength, size;
char *line;
char *contents = NULL;
nc_string *payload;
// pass 1: calculate buffer size
size = 0;
for (int i = 0; i < msg->length; i++) {
size += strlen(msg->lines[i]) + 1;
}
contents = malloc(size);
if (!contents) {
telem_log(LOG_ERR, "Call to malloc failed");
oops_processed = true;
return;
}
// pass 2: copy strings into the buffer
size_t done = 0;
char *bp = contents;
for (int i = 0; i < msg->length; i++) {
//Add the newline character to the end of each line
line = msg->lines[i];
linelength = strlen(line);
line[linelength] = '\n';
linelength++;
strncpy(bp, line, linelength);
bp[linelength] = '\n';
done += linelength + 1;
bp = contents + done;
}
// Write each line of oops to file
writer = write(fd, line, linelength);
if (writer == -1) {
telem_log(LOG_ERR, "Error writing buffer to file: %s\n", strerror(errno));
exit(EXIT_FAILURE);
struct oops_log_msg oops_msg;
if (handle_entire_oops(contents, (long)size, &oops_msg)) {
#ifdef DEBUG
printf("Raw message:\n");
for (int i = 0; i < oops_msg.length; i++) {
printf("%s\n", oops_msg.lines[i]);
}
#endif
payload = parse_payload(&oops_msg);
#ifdef DEBUG
printf("Payload Parsed :%s\n", payload->str);
#endif
oops_msg_cleanup(&oops_msg);
send_data(payload->str, (char *)oops_msg.pattern->classification, (uint32_t)oops_msg.pattern->severity);
nc_string_free(payload);
} else {
/* File does no contain an oops! */
telem_log(LOG_ERR, "Did not find an oops in the oops directory\n");
}
telem_log(LOG_INFO, "Oops written to file\n");
oops_written = true;
close(fd);
// Allocate more space to prepare for another oops
if (allocate_filespace() < 0) {
telem_log(LOG_ERR, "%s\n", strerror(errno));
exit(EXIT_FAILURE);
}
free(contents);
oops_processed = true;
}
bool get_oops_written(void)
void signal_handler_success(int signum)
{
return oops_written;
exit(EXIT_SUCCESS);
}
char *get_file_name(void)
{
return filename;
}
__attribute__((destructor))
void clean_up(void)
{
// Unlink empty file and close any open file
unlink(filename);
close(fd);
}
void signal_handler(int signum)
void signal_handler_fail(int signum)
{
exit(EXIT_FAILURE);
}
+17 -21
View File
@@ -1,7 +1,7 @@
/*
* This program is part of the Clear Linux Project
*
* Copyright 2015 Intel Corporation
* Copyright © 2015-2019 Intel Corporation
*
* This program is free software; you can redistribute it and/or modify it under
* the terms and conditions of the GNU Lesser General Public License, as
@@ -17,45 +17,41 @@
#pragma once
#include "oops_parser.h"
/**
* Process the buffer and send it to the backend
*
* @param *bufp A pointer to a buffer containing the bytes read
* from the kernel ring buffer
* @param bytes Number of bytes read from ring buffer into bufp
*
* @return 0 on succes, -1 on failure
*
*/
int klog_process_buffer(char *bufp, int bytes);
/**
* Splits the buffer into individual lines
*
* @param *bufp A pointer to a buffer containing the bytes read
* from the kernel ring buffer
* @param bytes Bytes read from ring buffer into bufp
* @param bytes Number of bytes read from ring buffer into bufp
*
*/
void split_buf_by_line(char *bufp, int bytes);
/**
* Allocates memory for oops message to write to file
*
* @return 0 on succes, -1 on failure
*/
int allocate_filespace(void);
/**
* Writes the oops message to tmp file
* Process the oops message and send it to the backend
*
* @param msg Struct that contains oops message lines and linecount
*
*/
void write_oops_to_file(struct oops_log_msg *msg);
void klog_process_oops_msgs(struct oops_log_msg *msg);
/**
* Signal handler for cleanup purposes
*
* @param signum An integer for the signal type
*
*/
void signal_handler(int signum);
/**
* For Unit Tests Purposes
* Determines whether an oops has been written to a file
*
* @return true if oops is written, false if not
*/
bool get_oops_written(void);
void signal_handler_fail(int signum);
void signal_handler_success(int signum);
/* vi: set ts=8 sw=8 sts=4 et tw=80 cino=(0: */
+7 -20
View File
@@ -4,10 +4,16 @@ bin_PROGRAMS += \
%D%/telem-record-gen \
%D%/klogscanner \
%D%/pstoreprobe \
%D%/oopsprobe \
%D%/pythonprobe \
%D%/pstoreclean \
%D%/bertprobe
%C%_pythonprobe_SOURCES = %D%/python-probe.c
%C%_pythonprobe_LDADD = $(top_builddir)/src/libtelemetry.la
%C%_pythonprobe_LDFLAGS = \
$(AM_LDFLAGS) \
-pie
%C%_hprobe_SOURCES = %D%/hello.c
%C%_hprobe_LDADD = $(top_builddir)/src/libtelemetry.la
%C%_hprobe_LDFLAGS = \
@@ -122,25 +128,6 @@ if HAVE_SYSTEMD_JOURNAL
endif
endif
%C%_oopsprobe_SOURCES = \
%D%/oops_probe.c \
%D%/oops_parser.c \
src/nica/nc-string.c \
%D%/probe.h
%C%_oopsprobe_CFLAGS = \
$(AM_CFLAGS)
%C%_oopsprobe_LDADD = \
$(top_builddir)/src/libtelemetry.la \
$(top_builddir)/src/libtelem-shared.la
if LOG_SYSTEMD
if HAVE_SYSTEMD_JOURNAL
%C%_oopsprobe_CFLAGS += \
$(SYSTEMD_JOURNAL_CFLAGS)
%C%_oopsprobe_LDADD += \
$(SYSTEMD_JOURNAL_LIBS)
endif
endif
if HAVE_SYSTEMD_JOURNAL
if HAVE_SYSTEMD_ID128
+18 -21
View File
@@ -1,7 +1,7 @@
/*
* This program is part of the Clear Linux Project
*
* Copyright 2015 Intel Corporation
* Copyright 2015-2019 Intel Corporation
*
* This program is free software; you can redistribute it and/or modify it under
* the terms and conditions of the GNU Lesser General Public License, as
@@ -152,9 +152,9 @@ struct oops_pattern oops_patterns_arr[] = {
},
};
int oops_patterns_cnt = sizeof(oops_patterns_arr) / sizeof(struct oops_pattern);
static int oops_patterns_cnt = sizeof(oops_patterns_arr) / sizeof(struct oops_pattern);
char *skip_log_level(char *line)
static char *skip_log_level(char *line)
{
char *start = line;
@@ -169,7 +169,7 @@ char *skip_log_level(char *line)
return start;
}
char *skip_timestamp(char *line)
static char *skip_timestamp(char *line)
{
char *start = line;
@@ -184,7 +184,7 @@ char *skip_timestamp(char *line)
return start;
}
char *skip_space(char *line)
static char *skip_space(char *line)
{
char *start = line;
@@ -194,7 +194,7 @@ char *skip_space(char *line)
return start;
}
char *skip_spaces(char *line)
static char *skip_spaces(char *line)
{
char *start = line;
@@ -204,7 +204,7 @@ char *skip_spaces(char *line)
return start;
}
bool starts_with(const char *line, const char *line_end, const char *substr)
static bool starts_with(const char *line, const char *line_end, const char *substr)
{
size_t substrlen = strlen(substr);
@@ -214,7 +214,7 @@ bool starts_with(const char *line, const char *line_end, const char *substr)
return false;
}
bool str_starts_with_casei(const char *line, const char *substr)
static bool str_starts_with_casei(const char *line, const char *substr)
{
size_t len = strlen(substr);
@@ -224,7 +224,7 @@ bool str_starts_with_casei(const char *line, const char *substr)
return false;
}
bool pattern_matches(char *line, char *line_end, struct oops_pattern *pattern)
static bool pattern_matches(char *line, char *line_end, struct oops_pattern *pattern)
{
if (pattern->is_regex) {
return (regexec(&(pattern->regex), line, 0, NULL, 0) == 0);
@@ -233,7 +233,7 @@ bool pattern_matches(char *line, char *line_end, struct oops_pattern *pattern)
}
}
void init_pattern_regex(void)
static void init_pattern_regex(void)
{
for (int i = 0; i < oops_patterns_cnt; i++) {
struct oops_pattern *pattern = &oops_patterns_arr[i];
@@ -244,7 +244,7 @@ void init_pattern_regex(void)
}
}
void free_pattern_regex(void)
static void free_pattern_regex(void)
{
for (int i = 0; i < oops_patterns_cnt; i++) {
struct oops_pattern *pattern = &oops_patterns_arr[i];
@@ -261,7 +261,6 @@ bool handle_entire_oops(char *buf, long size, struct oops_log_msg *msg)
int i;
msg->length = 0;
init_pattern_regex();
line_end = memchr(buf, '\n', (size_t)size);
if (line_end == NULL) {
@@ -318,7 +317,6 @@ void oops_msg_cleanup(struct oops_log_msg *msg)
free(msg->lines[i]);
}
msg->length = 0;
free_pattern_regex();
}
static struct oops_log_msg oops_msg;
@@ -333,7 +331,7 @@ void oops_parser_init(oops_handler_t handler)
init_pattern_regex();
}
void handle_msg_end(void)
static void handle_msg_end(void)
{
for (int i = 0; i < oops_msg.length; i++) {
free(oops_msg.lines[i]);
@@ -426,7 +424,6 @@ void parse_single_line(char *line, size_t size)
}
}
}
}
struct stack_frame {
@@ -437,7 +434,7 @@ struct stack_frame {
struct stack_frame *next;
};
void stack_frame_append(struct stack_frame **head, struct stack_frame **tail, char *start)
static void stack_frame_append(struct stack_frame **head, struct stack_frame **tail, char *start)
{
/*
* Format is:
@@ -536,7 +533,7 @@ void stack_frame_append(struct stack_frame **head, struct stack_frame **tail, ch
frame->module = "kernel";
}
void stack_frame_free(struct stack_frame **head)
static void stack_frame_free(struct stack_frame **head)
{
struct stack_frame *frame;
@@ -553,7 +550,7 @@ void stack_frame_free(struct stack_frame **head)
* CPU: 2 PID: 0 Comm: swapper/2 Not tainted 3.10.4-100.fc18.x86_64 #1
* CPU: 3 PID: 0 Comm: swapper/3 Not tainted 4.0.5-300.fc22.x86_64 #1
*/
void parse_kernel_cpu_line(char *line, char **kernel_version, char **tainted)
static void parse_kernel_cpu_line(char *line, char **kernel_version, char **tainted)
{
char *end = NULL;
@@ -626,7 +623,7 @@ static struct reg_s *reg_head = NULL;
*
*/
void parse_registers(char *line)
static void parse_registers(char *line)
{
uint64_t value;
struct reg_s *reg_entry = NULL;
@@ -739,7 +736,7 @@ void parse_registers(char *line)
}
void append_registers_to_bt(nc_string **backtrace)
static void append_registers_to_bt(nc_string **backtrace)
{
struct reg_s *reg_entry = reg_head;
@@ -762,7 +759,7 @@ void append_registers_to_bt(nc_string **backtrace)
}
}
nc_string *parse_backtrace(struct oops_log_msg *msg)
static nc_string *parse_backtrace(struct oops_log_msg *msg)
{
struct stack_frame *head = NULL, *tail = NULL, *elem = NULL;
//int in_stack_dump = 0;
-229
View File
@@ -1,229 +0,0 @@
/*
* This program is part of the Clear Linux Project
*
* Copyright 2015 Intel Corporation
*
* This program is free software; you can redistribute it and/or modify it under
* the terms and conditions of the GNU Lesser General Public License, as
* published by the Free Software Foundation; either version 2.1 of the License,
* or (at your option) any later version.
*
* This program is distributed in the hope it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*/
#define _GNU_SOURCE
#include <stdio.h>
#include <sys/types.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <sys/inotify.h>
#include <poll.h>
#include "oops_parser.h"
#include "log.h"
#include "telemetry.h"
#include "nica/nc-string.h"
char *oops_dir_path = KERNELOOPSDIR;
static uint32_t version = 1;
static bool send_data(char *backtrace, char *class, uint32_t severity)
{
struct telem_ref *handle = NULL;
int ret;
if ((ret = tm_create_record(&handle, severity, class, version)) < 0) {
telem_log(LOG_ERR, "Failed to create record: %s",
strerror(-ret));
return false;
}
if ((ret = tm_set_payload(handle, backtrace)) < 0) {
telem_log(LOG_ERR, "Failed to set payload: %s", strerror(-ret));
tm_free_record(handle);
return false;
}
if ((ret = tm_send_record(handle)) < 0) {
telem_log(LOG_ERR, "Failed to send record: %s", strerror(-ret));
tm_free_record(handle);
return false;
}
tm_free_record(handle);
return true;
}
void handle_oops_file(const char *fname)
{
int ret;
FILE *fp = NULL;
char *filename = NULL, *contents = NULL;
long sz;
size_t size, bytes_read;
nc_string *payload;
ret = asprintf(&filename, "%s/%s", oops_dir_path, fname);
if (ret == -1) {
exit(EXIT_FAILURE);
}
#ifdef DEBUG
printf("File to be handled: %s\n", filename);
#endif
fp = fopen(filename, "r");
if (fp == NULL) {
telem_log(LOG_ERR, "Failed to open oops log file %s: %s", fname, strerror(errno));
goto file_end;
}
if (fseek(fp, 0, SEEK_END) == -1) {
telem_log(LOG_ERR, "Failed to seek file %s: %s\n", fname, strerror(errno));
goto file_end;
}
sz = ftell(fp);
if (sz == -1L) {
telem_perror("ftell failed");
goto file_end;
}
/* If file is empty */
if (sz == 0L) {
telem_perror("File is empty\n");
goto file_end;
}
if (fseek(fp, 0, SEEK_SET) < 0) {
telem_log(LOG_ERR, "Fseek failed for file %s:%s\n", fname, strerror(errno));
goto file_end;
}
size = (size_t)sz;
contents = malloc(size);
if (!contents) {
telem_log(LOG_ERR, "Call to malloc failed");
goto file_end;
}
memset(contents, 0, size);
bytes_read = fread(contents, sizeof(char), size, fp);
if (bytes_read < size) {
telem_log(LOG_ERR, "Error while reading file %s:%s\n", fname, strerror(errno));
goto file_end;
}
struct oops_log_msg oops_msg;
if (handle_entire_oops(contents, (long)size, &oops_msg)) {
#ifdef DEBUG
printf("Raw message:\n");
for (int i = 0; i < oops_msg.length; i++) {
printf("%s\n", oops_msg.lines[i]);
}
#endif
payload = parse_payload(&oops_msg);
#ifdef DEBUG
printf("Payload Parsed :%s\n", payload->str);
#endif
oops_msg_cleanup(&oops_msg);
send_data(payload->str, (char *)oops_msg.pattern->classification, (uint32_t)oops_msg.pattern->severity);
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");
}
file_end:
if (contents) {
free(contents);
}
if (fp) {
fclose(fp);
}
if (unlink(filename) == -1) {
telem_log(LOG_ERR, "Failed to unlink %s: %s\n", filename, strerror(errno));
}
free(filename);
}
void handle_inotify_event(const struct inotify_event *event)
{
if (!event) {
telem_perror("Null event received");
return;
}
if (event->len == 0) {
telem_perror("inotify event received with no file.");
return;
}
if (event->mask & IN_CLOSE_WRITE) {
if (event->mask & IN_ISDIR) {
return;
}
#ifdef DEBUG
telem_log(LOG_DEBUG, "New file written to the oops location: %s\n", event->name);
#endif
handle_oops_file(event->name);
}
}
int main(int argc, char **argv)
{
int inotify_fd;
char buf[4096];
char *ptr = NULL;
ssize_t len;
const struct inotify_event *event;
inotify_fd = inotify_init();
if (inotify_fd == -1) {
telem_log(LOG_ERR, "Failed to create inotify instance: %s\n", strerror(errno));
exit(EXIT_FAILURE);
}
if (inotify_add_watch(inotify_fd, oops_dir_path, IN_CLOSE_WRITE) == -1) {
telem_perror("Error adding watch to the inotify instance");
}
while (1) {
len = read(inotify_fd, buf, sizeof buf);
if (len <= 0) {
telem_perror("error reading event");
continue;
}
if (len < sizeof(struct inotify_event)) {
telem_perror("Incomplete event received");
continue;
}
telem_log(LOG_INFO, "Got inotify event/s\n");
/* Loop over all events in the buffer */
for (ptr = buf; ptr < buf + len;
ptr += sizeof(struct inotify_event) + event->len) {
event = (const struct inotify_event *)ptr;
/* handle the event */
handle_inotify_event(event);
}
}
return 0;
}
/* vi: set ts=8 sw=8 sts=4 et tw=80 cino=(0: */
+293
View File
@@ -0,0 +1,293 @@
/*
* This program is part of the Clear Linux Project
*
* Copyright 2019 Intel Corporation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#define _GNU_SOURCE
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/inotify.h>
#include <fcntl.h>
#include <dirent.h>
#include <pwd.h>
#include <grp.h>
#include <getopt.h>
#include "log.h"
#include "telemetry.h"
#define PYTHON_TELEMETRY_DIR "/var/lib/telemetry/python"
/* We expect something like: 1.python-exception.3.N2Qy24 */
static bool validate_file_name(const char *filename, uint32_t *severity,
char** class, uint32_t *version)
{
unsigned int ver, sev;
char cls[PATH_MAX] = { 0 };
char rnd[PATH_MAX] = { 0 };
char *filename_base = basename(filename);
char *str_class = NULL;
if (!filename_base) {
telem_perror("basename failed");
return false;
}
if (sscanf(filename_base,"%u.%[^]0-9.].%u.%s", &ver, cls, &sev, rnd) == 4) {
if (asprintf(&str_class, "org.clearlinux/python/%s", cls) < 29) {
telem_log(LOG_ERR, "%s: invalid telemetry classification\n",
str_class);
return false;
}
*class = str_class;
*version = ver;
*severity = sev;
return true;
}
return false;
}
static char *read_file_into_buffer(const char *filename)
{
FILE *fp = NULL;
char *contents = NULL;
long sz;
size_t size, bytes_read;
fp = fopen(filename, "r");
if (fp == NULL) {
telem_log(LOG_ERR, "Failed to open Python telemetry file %s: %s\n",
filename, strerror(errno));
return NULL;
}
if (fseek(fp, 0, SEEK_END) == -1) {
telem_log(LOG_ERR, "Failed to seek file %s: %s\n", filename,
strerror(errno));
goto error_end;
}
sz = ftell(fp);
if (sz == -1L) {
telem_perror("ftell failed");
goto error_end;
}
if (sz == 0L) {
telem_perror("File is empty\n");
goto error_end;
}
if (fseek(fp, 0, SEEK_SET) < 0) {
telem_log(LOG_ERR, "fseek failed for file %s:%s\n", filename,
strerror(errno));
goto error_end;
}
size = (size_t)sz;
contents = calloc(sizeof(char), size);
if (!contents) {
telem_log(LOG_ERR, "Call to calloc failed\n");
goto error_end;
}
bytes_read = fread(contents, sizeof(char), size, fp);
if (bytes_read < size) {
telem_log(LOG_ERR, "Error while reading file %s:%s\n", filename,
strerror(errno));
goto error_end;
}
fclose(fp);
return contents;
error_end:
fclose(fp);
if (contents) {
free(contents);
}
return NULL;
}
static void send_data(char *contents, uint32_t severity, char *class,
uint32_t version)
{
struct telem_ref *handle = NULL;
int ret;
if ((ret = tm_create_record(&handle, severity, class, version)) < 0) {
telem_log(LOG_ERR, "Failed to create record: %s\n",
strerror(-ret));
return;
}
if ((ret = tm_set_payload(handle, contents)) < 0) {
telem_log(LOG_ERR, "Failed to set payload: %s\n",
strerror(-ret));
tm_free_record(handle);
return;
}
if ((ret = tm_send_record(handle)) < 0) {
telem_log(LOG_ERR, "Failed to send record: %s\n",
strerror(-ret));
}
tm_free_record(handle);
}
/* Send a valid file to the backend. */
static void deliver_payload(const char *filename)
{
uint32_t severity;
uint32_t version;
char *class = NULL;
char *contents = NULL;
if (validate_file_name(filename, &severity, &class, &version)) {
if ((contents = read_file_into_buffer(filename)) != NULL) {
send_data(contents, severity, class, version);
free(contents);
}
free(class);
}
/* Keep PYTHON_TELEMETRY_DIR empty. Delete the file. */
if (unlink(filename) != 0) {
telem_log(LOG_ERR, "Failed to unlink %s: %s\n", filename,
strerror(errno));
}
}
static void drop_privs(void)
{
uid_t euid;
euid = geteuid();
if (euid != 0) {
telem_log(LOG_DEBUG, "Not root; skipping privilege drop\n");
return;
}
struct passwd *pw;
pw = getpwnam("telemetry");
if (!pw) {
telem_log(LOG_ERR, "telemetry user not found\n");
exit(EXIT_FAILURE);
}
// The order is important here:
// change supplemental groups, our gid, and then our uid
if (initgroups(pw->pw_name, pw->pw_gid) != 0) {
telem_perror("Failed to set supplemental group list");
exit(EXIT_FAILURE);
}
if (setgid(pw->pw_gid) != 0) {
telem_perror("Failed to set GID");
exit(EXIT_FAILURE);
}
if (setuid(pw->pw_uid) != 0) {
telem_perror("Failed to set UID");
exit(EXIT_FAILURE);
}
assert(getuid() == pw->pw_uid);
assert(geteuid() == pw->pw_uid);
assert(getgid() == pw->pw_gid);
assert(getegid() == pw->pw_gid);
}
static void print_usage(char *prog)
{
printf("%s: Usage\n", prog);
printf(" -f, --config_file Specify a configuration file other than default\n");
printf(" -h, --help Display this help message\n");
printf(" -V, --version Print the program version\n");
}
int main(int argc, char **argv)
{
DIR *dir;
struct dirent *entry;
int c;
int opt_index = 0;
struct option opts[] = {
{ "config_file", 1, NULL, 'f' },
{ "help", 0, NULL, 'h' },
{ "version", 0, NULL, 'V' },
{ NULL, 0, NULL, 0 }
};
drop_privs();
while ((c = getopt_long(argc, argv, "f:hV", opts, &opt_index)) != -1) {
switch (c) {
case 'f':
if (tm_set_config_file(optarg) != 0) {
telem_log(LOG_ERR, "Configuration file"
" path not valid\n");
exit(EXIT_FAILURE);
}
break;
case 'h':
print_usage(argv[0]);
exit(EXIT_SUCCESS);
case 'V':
printf(PACKAGE_VERSION "\n");
exit(EXIT_SUCCESS);
case '?':
exit(EXIT_FAILURE);
}
}
if (chdir(PYTHON_TELEMETRY_DIR)) {
perror(PYTHON_TELEMETRY_DIR);
exit(EXIT_FAILURE);
}
/* Process all existing files */
dir = opendir(PYTHON_TELEMETRY_DIR);
if (!dir) {
perror(PYTHON_TELEMETRY_DIR);
exit(EXIT_FAILURE);
}
while (true) {
entry = readdir(dir);
if (!entry) {
break;
}
if (entry->d_name[0] == '.') {
continue;
}
deliver_payload(entry->d_name);
}
closedir(dir);
exit(EXIT_SUCCESS);
}
+71 -28
View File
@@ -39,10 +39,13 @@ static char *opt_payload = NULL;
static uint32_t payload_version = 1;
static char *opt_payload_file = NULL;
static char *opt_event_id = NULL;
static bool opt_echo = false;
static bool opt_nopost = false;
static const struct option prog_opts[] = {
{ "help", no_argument, 0, 'h' },
{ "config-file", required_argument, 0, 'f' },
{ "echo", no_argument, 0, 'o' },
{ "no-post", no_argument, 0, 'n' },
{ "version", no_argument, 0, 'V' },
{ "severity", required_argument, 0, 's' },
{ "class", required_argument, 0, 'c' },
@@ -62,14 +65,15 @@ static void print_help(void)
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(" -c, --class Classification level_1/level_2/level_3 (required)\n");
printf(" -p, --payload Record body (max size = 8k) (required)\n");
printf(" -P, --payload-file File to read payload from\n");
printf(" -R, --record-version Version number for format of payload (default 1)\n");
printf(" -e, --event-id Event id to use in the record\n");
printf(" -o, --echo Echo record to stdout\n");
printf(" -n, --no-post Do not post record just print\n");
printf("\n");
}
@@ -94,7 +98,7 @@ int parse_options(int argc, char **argv)
long unsigned int tmp = 0;
int opt;
while ((opt = getopt_long(argc, argv, "hc:Vs:c:p:P:R:e:", prog_opts, NULL)) != -1) {
while ((opt = getopt_long(argc, argv, "hc:Vs:c:p:P:R:e:on", prog_opts, NULL)) != -1) {
switch (opt) {
case 'h':
print_help();
@@ -149,6 +153,12 @@ int parse_options(int argc, char **argv)
goto fail;
}
break;
case 'o':
opt_echo = true;
break;
case 'n':
opt_nopost = true;
break;
}
}
@@ -218,13 +228,13 @@ int allocate_payload_buffer(char **payload)
{
int ret = 0;
*payload = (char *)malloc(MAX_PAYLOAD_SIZE);
*payload = (char *)malloc(MAX_PAYLOAD_LENGTH);
if (*payload == NULL) {
goto out1;
}
*payload = memset(*payload, 0, MAX_PAYLOAD_SIZE);
*payload = memset(*payload, 0, MAX_PAYLOAD_LENGTH);
ret = 1;
out1:
@@ -246,7 +256,7 @@ int get_payload_from_file(char **payload)
}
bytes_in = fread(*payload, 1,
MAX_PAYLOAD_SIZE - 1, fp);
MAX_PAYLOAD_LENGTH - 1, fp);
/* if fread fails */
if (bytes_in == 0 && ferror(fp) != 0) {
@@ -267,8 +277,8 @@ void get_payload_from_opt(char **payload)
size_t len = 0;
len = strlen(opt_payload);
if (len >= MAX_PAYLOAD_SIZE) {
len = MAX_PAYLOAD_SIZE - 1;
if (len >= MAX_PAYLOAD_LENGTH) {
len = MAX_PAYLOAD_LENGTH - 1;
}
strncpy(*payload, opt_payload, len);
}
@@ -278,9 +288,9 @@ void get_payload_from_stdin(char **payload)
size_t bytes_in = 0;
int c;
bytes_in = fread(*payload, 1, MAX_PAYLOAD_SIZE - 1, stdin);
bytes_in = fread(*payload, 1, MAX_PAYLOAD_LENGTH - 1, stdin);
if (bytes_in == MAX_PAYLOAD_SIZE - 1) {
if (bytes_in == MAX_PAYLOAD_LENGTH - 1) {
/* Throw away the rest of stdin */
while (EOF != (c = fgetc(stdin))) {
continue;
@@ -322,34 +332,62 @@ out1:
return ret;
}
int send_record(char *payload)
int instanciate_record(struct telem_ref **t_ref, char *payload)
{
struct telem_ref *t_ref;
int ret = 0;
if (tm_create_record(&t_ref, (uint32_t)severity,
opt_class, payload_version) < 0) {
if ((ret = tm_create_record(t_ref, (uint32_t)severity,
opt_class, payload_version)) < 0) {
goto out1;
}
if (opt_event_id && tm_set_event_id(t_ref, opt_event_id) < 0) {
goto out2;
if ((ret = opt_event_id && tm_set_event_id(*t_ref, opt_event_id)) < 0) {
goto out1;
}
if (tm_set_payload(t_ref, payload) < 0) {
goto out2;
if ((ret = tm_set_payload(*t_ref, payload)) < 0) {
goto out1;
}
if (tm_send_record(t_ref) >= 0) {
ret = 1;
}
out2:
tm_free_record(t_ref);
out1:
return ret;
}
int send_record(char *payload)
{
struct telem_ref *t_ref = NULL;
int ret = 0;
if ((ret = instanciate_record(&t_ref, payload)) < 0) {
goto out;
}
if ((ret = tm_send_record(t_ref)) < 0) {
goto out;
}
out:
tm_free_record(t_ref);
return ret;
}
int print_record(char *payload)
{
struct telem_ref *t_ref = NULL;
int ret = 0;
int i = 0;
if ((ret = instanciate_record(&t_ref, payload)) == 0) {
for (i = 0; i < NUM_HEADERS; i++) {
fprintf(stdout, "%s", t_ref->record->headers[i]);
}
fprintf(stdout, "%s\n", t_ref->record->payload);
tm_free_record(t_ref);
}
return ret;
}
int main(int argc, char **argv)
{
int ret = EXIT_FAILURE;
@@ -367,7 +405,11 @@ int main(int argc, char **argv)
goto fail;
}
if (!send_record(payload)) {
if (opt_echo == true) {
print_record(payload);
}
if (opt_nopost == false && send_record(payload) != 0) {
goto fail;
}
@@ -377,6 +419,7 @@ fail:
free(opt_class);
free(opt_payload);
free(opt_event_id);
free(payload);
return ret;
}
+3 -3
View File
@@ -27,7 +27,7 @@
#include <errno.h>
#include "spool.h"
#include "telemdaemon.h"
#include "telempostdaemon.h"
#include "log.h"
#include "configuration.h"
#include "util.h"
@@ -221,7 +221,7 @@ void transmit_spooled_record(char *record_path, bool *post_succeeded, long size)
}
//Get rid of trailing newline
strtok(line, "\n");
if (get_header(line, header_name, &headers[i], strlen(header_name))) {
if (get_header(line, header_name, &headers[i])) {
continue;
} else {
telem_log(LOG_ERR, "transmit_spooled_record: Incorrect"
@@ -254,7 +254,7 @@ void transmit_spooled_record(char *record_path, bool *post_succeeded, long size)
goto read_error;
}
*post_succeeded = post_record_http(headers, payload, false);
*post_succeeded = post_record_http(headers, payload);
if (*post_succeeded) {
unlink(record_path);
}
+11 -396
View File
@@ -29,12 +29,12 @@
#include <malloc.h>
#include <sys/uio.h>
#include "iorecord.h"
#include "telemdaemon.h"
#include "common.h"
#include "util.h"
#include "log.h"
#include "configuration.h"
#include "retention.h"
void initialize_daemon(TelemDaemon *daemon)
{
@@ -43,32 +43,7 @@ void initialize_daemon(TelemDaemon *daemon)
daemon->nfds = 0;
daemon->pollfds = NULL;
daemon->client_head = head;
daemon->bypass_http_post_ts = 0;
daemon->is_spool_valid = false;
daemon->record_journal = open_journal(JOURNAL_PATH);
initialize_rate_limit(daemon);
initialize_record_delivery(daemon);
daemon->current_spool_size = 0;
}
void initialize_rate_limit(TelemDaemon *daemon)
{
for (int i = 0; i < TM_RATE_LIMIT_SLOTS; i++) {
daemon->record_burst_array[i] = 0;
daemon->byte_burst_array[i] = 0;
}
daemon->rate_limit_enabled = rate_limit_enabled_config();
daemon->record_burst_limit = record_burst_limit_config();
daemon->record_window_length = record_window_length_config();
daemon->byte_burst_limit = byte_burst_limit_config();
daemon->byte_window_length = byte_window_length_config();
daemon->rate_limit_strategy = rate_limit_strategy_config();
}
void initialize_record_delivery(TelemDaemon *daemon)
{
daemon->record_retention_enabled = record_retention_enabled_config();
daemon->record_server_delivery_enabled = record_server_delivery_enabled_config();
daemon->machine_id_override = NULL;
}
client *add_client(client_list_head *client_head, int fd)
@@ -272,42 +247,18 @@ void machine_id_replace(char **machine_header, char *machine_id_override)
free(old_header);
}
void save_entry_to_journal(TelemDaemon *daemon, time_t t_stamp, char *headers[])
{
char *classification_value;
char *event_id_value;
if (get_header_value(headers[TM_CLASSIFICATION], &classification_value) &&
get_header_value(headers[TM_EVENT_ID], &event_id_value)) {
if (new_journal_entry(daemon->record_journal, classification_value, t_stamp, event_id_value) != 0) {
telem_log(LOG_INFO, "new_journal_entry in process_record: failed saving record entry\n");
}
}
free(classification_value);
free(event_id_value);
}
void process_record(TelemDaemon *daemon, client *cl)
{
int i = 0;
int ret = 0;
char *headers[NUM_HEADERS];
bool do_spool = false;
bool record_sent = false;
char *tok = NULL;
size_t header_size = 0;
size_t message_size = 0;
char *temp_headers = NULL;
char *msg;
char *body;
int current_minute = 0;
bool record_check_passed = true;
bool byte_check_passed = true;
bool record_burst_enabled = true;
bool byte_burst_enabled = true;
/* Gets the current minute of time */
time_t temp = time(NULL);
struct tm *tm_s = localtime(&temp);
current_minute = tm_s->tm_min;
char *recordpath = NULL;
header_size = *(uint32_t *)cl->buf;
message_size = cl->size - header_size;
@@ -321,7 +272,7 @@ void process_record(TelemDaemon *daemon, client *cl)
for (i = 0; i < NUM_HEADERS; i++) {
const char *header_name = get_header_name(i);
if (get_header(tok, header_name, &headers[i], strlen(header_name))) {
if (get_header(tok, header_name, &headers[i])) {
if (strcmp(header_name, TM_MACHINE_ID_STR) == 0) {
machine_id_replace(&headers[i], daemon->machine_id_override);
}
@@ -335,83 +286,15 @@ void process_record(TelemDaemon *daemon, client *cl)
/* TODO : check if the body is within the limits. */
body = msg + header_size;
/* Save record in journal */
save_entry_to_journal(daemon, temp, headers);
/* Save local copy */
if (daemon->record_retention_enabled) {
save_local_copy(daemon, body);
}
/* Bail out if server delivery is not enabled */
if (!daemon->record_server_delivery_enabled) {
#ifdef DEBUG
fprintf(stdout, "process_record: record server delivery disabled\n");
#endif
goto end;
}
if (inside_direct_spool_window(daemon, time(NULL))) {
telem_log(LOG_INFO, "process_record: delivering directly to spool\n");
spool_record(daemon, headers, body);
goto end;
}
if (daemon->record_window_length == -1 ||
daemon->byte_window_length == -1) {
telem_log(LOG_ERR, "Invalid value for window length\n");
/* Save record to stage */
ret = asprintf(&recordpath, "%s/XXXXXX", spool_dir_config());
if (ret == -1) {
telem_log(LOG_ERR, "Failed to allocate memory for record name in staging folder, aborting\n");
exit(EXIT_FAILURE);
}
/* Checks if entirety of rate limiting is enabled */
if (daemon->rate_limit_enabled) {
/* Checks whether record and byte bursts are enabled individually */
record_burst_enabled = burst_limit_enabled(daemon->record_burst_limit);
byte_burst_enabled = burst_limit_enabled(daemon->byte_burst_limit);
if (record_burst_enabled) {
record_check_passed = rate_limit_check(current_minute,
daemon->record_burst_limit, daemon->record_window_length,
daemon->record_burst_array, TM_RECORD_COUNTER);
}
if (byte_burst_enabled) {
byte_check_passed = rate_limit_check(current_minute,
daemon->byte_burst_limit, daemon->byte_window_length,
daemon->byte_burst_array, cl->size);
}
/* If both record and byte burst disabled, rate limiting disabled */
if (!record_burst_enabled && !byte_burst_enabled) {
daemon->rate_limit_enabled = false;
}
}
/* Sends record if rate limiting is disabled, or all checks passed */
if (!daemon->rate_limit_enabled || (record_check_passed && byte_check_passed)) {
/* Send the record as https post */
record_sent = post_record_ptr(headers, body, true);
}
// Get rate-limit strategy
do_spool = spool_strategy_selected(daemon);
// Drop record
if (!record_sent && !do_spool) {
goto end;
}
// Spool Record
else if (!record_sent && do_spool) {
start_network_bypass(daemon);
telem_log(LOG_INFO, "process_record: initializing direct-spool window\n");
spool_record(daemon, headers, body);
} else {
/* Updates rate limiting arrays if record sent */
if (record_burst_enabled) {
rate_limit_update (current_minute, daemon->record_window_length,
daemon->record_burst_array, TM_RECORD_COUNTER);
}
if (byte_burst_enabled) {
rate_limit_update (current_minute, daemon->byte_window_length,
daemon->byte_burst_array, cl->size);
}
}
stage_record(recordpath, headers, body);
free(recordpath);
end:
free(temp_headers);
for (int k = 0; k < i; k++)
@@ -419,274 +302,6 @@ end:
return;
}
bool burst_limit_enabled(int64_t burst_limit)
{
return (burst_limit > -1) ? true : false;
}
bool rate_limit_check(int current_minute, int64_t burst_limit, int
window_length, size_t *array, size_t incValue)
{
size_t count = 0;
/* Variable to determine last minute allowed in burst window */
int window_start = (TM_RATE_LIMIT_SLOTS + (current_minute - window_length + 1))
% TM_RATE_LIMIT_SLOTS;
/* The modulo is not placed in the for loop inself, because if
* the current minute is less than the window start (from wrapping)
* the for loop will never be entered.
*/
/* Counts all elements within burst_window in array */
for (int i = window_start; i < (window_start + window_length); i++) {
count += array[i % TM_RATE_LIMIT_SLOTS];
if ((array[i % TM_RATE_LIMIT_SLOTS] + incValue) >= SIZE_MAX) {
/* Exceeds maximum size array can hold */
return false;
}
}
/* Include current record being processed to count */
count += incValue;
/* Determines whether the count has exceeded the limit or not */
return (count > burst_limit) ? false : true;
}
void rate_limit_update(int current_minute, int window_length, size_t *array,
size_t incValue)
{
/* Variable to determine the amount of zero'd out spots needed */
int blank_slots = (TM_RATE_LIMIT_SLOTS - window_length);
/* Update specified array depending on increment value */
array[current_minute] += incValue;
/* Zero out expired records for record array */
for (int i = current_minute + 1; i < (blank_slots + current_minute + 1); i++) {
array[i % TM_RATE_LIMIT_SLOTS] = 0;
}
}
bool spool_strategy_selected(TelemDaemon *daemon)
{
/* Performs action depending on strategy choosen */
return (strcmp(daemon->rate_limit_strategy, "spool") == 0) ? true : false;
}
bool inside_direct_spool_window(TelemDaemon *daemon, time_t current_time)
{
return (current_time < daemon->bypass_http_post_ts + 1800) ? true : false;
}
void start_network_bypass(TelemDaemon *daemon)
{
daemon->bypass_http_post_ts = time(NULL);
}
size_t write_callback(char *ptr, size_t size, size_t nmemb, void *userdata)
{
telem_log(LOG_DEBUG, "Received data:\n%s\n", ptr);
return size * nmemb;
}
bool post_record_http(char *headers[], char *body, bool spool)
{
CURL *curl;
int res = 0;
char *content = "Content-Type: application/text";
struct curl_slist *custom_headers = NULL;
char errorbuf[CURL_ERROR_SIZE];
long http_response = 0;
const char *cert_file = get_cainfo_config();
const char *tid_header = get_tidheader_config();
// 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"
" easy session, exiting\n");
exit(EXIT_FAILURE);
/* TODO: check if memory needs to be released */
}
// Errors for any curl_easy_* functions will store nice error messages
// in errorbuf, so send log messages with errorbuf contents
curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errorbuf);
curl_easy_setopt(curl, CURLOPT_URL, server_addr_config());
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 5L);
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 10L);
curl_easy_setopt(curl, CURLOPT_POST, 1);
#ifdef DEBUG
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
#endif
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);
for (int i = 0; i < NUM_HEADERS; i++) {
custom_headers = curl_slist_append(custom_headers, headers[i]);
}
custom_headers = curl_slist_append(custom_headers, tid_header);
// This should be set by probes/libtelemetry in the future
custom_headers = curl_slist_append(custom_headers, content);
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, custom_headers);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, body);
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, strlen(body));
curl_easy_setopt(curl, CURLOPT_USE_SSL, CURLUSESSL_TRY);
if (strlen(cert_file) > 0) {
if (access(cert_file, F_OK) != -1) {
curl_easy_setopt(curl, CURLOPT_CAINFO, cert_file);
telem_log(LOG_INFO, "cafile was set to %s\n", cert_file);
}
}
telem_log(LOG_DEBUG, "Executing curl operation...\n");
errorbuf[0] = 0;
res = curl_easy_perform(curl);
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_response);
if (res) {
size_t len = strlen(errorbuf);
if (len) {
telem_log(LOG_ERR, "Failed sending record: %s%s", errorbuf,
((errorbuf[len - 1] != '\n') ? "\n" : ""));
} else {
telem_log(LOG_ERR, "Failed sending record: %s\n",
curl_easy_strerror(res));
}
} else if (http_response != 201 && http_response != 200) {
/* 201 means the record was successfully created
* 200 is a generic "ok".
* TODO: Make list of white-listed response codes configurable?
*/
telem_log(LOG_ERR, "Encountered error %ld on the server\n",
http_response);
// We treat HTTP error codes the same as libcurl errors
res = 1;
} else {
telem_log(LOG_INFO, "Record sent successfully\n");
}
curl_slist_free_all(custom_headers);
curl_easy_cleanup(curl);
curl_global_cleanup();
return res ? false : true;
}
void save_local_copy(TelemDaemon *daemon, char *body)
{
int ret = 0;
char *tmpbuf = NULL;
FILE *tmpfile = NULL;
if (daemon == NULL || daemon->record_journal == NULL ||
daemon->record_journal->latest_record_id == NULL) {
return;
}
ret = asprintf(&tmpbuf, "%s/%s", RECORD_RETENTION_DIR,
daemon->record_journal->latest_record_id);
if (ret == -1) {
telem_log(LOG_ERR, "Failed to allocate memory for record full path, aborting\n");
return;
}
tmpfile = fopen(tmpbuf, "w");
if (!tmpfile) {
telem_perror("Error opening local record copy temp file");
goto save_err;
}
// Save body
fprintf(tmpfile, "%s\n", body);
fclose(tmpfile);
save_err:
free(tmpbuf);
return;
}
void spool_record(TelemDaemon *daemon, char *headers[], char *body)
{
int ret = 0;
struct stat stat_buf;
int64_t max_spool_size = 0;
char *tmpbuf = NULL;
FILE *tmpfile = NULL;
int tmpfd = 0;
if (!daemon->is_spool_valid) {
/* If spool is not valid, simply drop the record */
return;
}
//check if the size is greater than 1 MB/spool max size
max_spool_size = spool_max_size_config();
if (max_spool_size != -1) {
telem_log(LOG_DEBUG, "Total size of spool dir: %ld\n", daemon->current_spool_size);
if (daemon->current_spool_size < 0) {
telem_log(LOG_ERR, "Error getting spool directory size: %s\n",
strerror(-(int)(daemon->current_spool_size)));
return;
} else if (daemon->current_spool_size >= (max_spool_size * 1024)) {
telem_log(LOG_INFO, "Spool dir full, dropping record\n");
return;
}
}
// create file with record
ret = asprintf(&tmpbuf, "%s/XXXXXX", spool_dir_config());
if (ret == -1) {
telem_log(LOG_ERR, "Failed to allocate memory for record name, aborting\n");
exit(EXIT_FAILURE);
}
tmpfd = mkstemp(tmpbuf);
if (tmpfd == -1) {
telem_perror("Error while creating temp file");
goto spool_err;
}
tmpfile = fdopen(tmpfd, "a");
if (!tmpfile) {
telem_perror("Error opening temp file");
close(tmpfd);
if (unlink(tmpbuf)) {
telem_perror("Error deleting temp file");
}
goto spool_err;
}
// write headers
for (int i = 0; i < NUM_HEADERS; i++) {
fprintf(tmpfile, "%s\n", headers[i]);
}
//write body
fprintf(tmpfile, "%s\n", body);
fflush(tmpfile);
fclose(tmpfile);
/* The stat should not fail here unless it is ENOMEM */
if (stat(tmpbuf, &stat_buf) == 0) {
daemon->current_spool_size += (stat_buf.st_blocks * 512);
}
spool_err:
free(tmpbuf);
return;
}
void add_pollfd(TelemDaemon *daemon, int fd, short events)
{
assert(daemon);
+1 -143
View File
@@ -22,7 +22,6 @@
#include <sys/queue.h>
#include <stdbool.h>
#include <inttypes.h>
#include "journal/journal.h"
#define TM_MACHINE_ID_EXPIRY (3 /*d*/ * 24 /*h*/ * 60 /*m*/ * 60 /*s*/)
@@ -55,26 +54,6 @@ typedef struct TelemDaemon {
size_t current_alloc;
/* client list head */
client_list_head client_head;
/* Time of last failed post */
time_t bypass_http_post_ts;
/* Telemetry Journal*/
TelemJournal *record_journal;
/* Rate limit record and byte arrays */
size_t record_burst_array[TM_RATE_LIMIT_SLOTS];
size_t byte_burst_array[TM_RATE_LIMIT_SLOTS];
/* Rate Limit Configurations */
bool rate_limit_enabled;
int64_t record_burst_limit;
int record_window_length;
int64_t byte_burst_limit;
int byte_window_length;
const char *rate_limit_strategy;
/* Spool configuration */
bool is_spool_valid;
long current_spool_size;
/* Record local copy and delivery */
bool record_retention_enabled;
bool record_server_delivery_enabled;
char *machine_id_override;
} TelemDaemon;
@@ -85,21 +64,6 @@ typedef struct TelemDaemon {
*/
void initialize_daemon(TelemDaemon *daemon);
/**
* Initialize rate-limiting in the daemon
*
* @param daemon A pointer to the daemon structure
*/
void initialize_rate_limit(TelemDaemon *daemon);
/**
* Initialize record delivery to remote and locally
* in the daemon
*
* @param daemon A pointer to the daemon structure
*/
void initialize_record_delivery(TelemDaemon *daemon);
/**
* Add poll fd struct to the array of pollfds.
*
@@ -182,113 +146,6 @@ void terminate_client(TelemDaemon *daemon, client *cl, nfds_t index);
*/
void process_record(TelemDaemon *daemon, client *cl);
/**
* Check if burst limiting is enabled
*
* @param burst_limit Burst limit amount. If negative rate limiting
* is disabled
*
* @return true if rate limiting for burst_limit variable is enabled
*/
bool burst_limit_enabled(int64_t burst_limit);
/**
* Checks whether record does not exceed the burst threshold
*
* @param current_minute The current minute of time the record processed
* @param burst_limit Amount of records that can be passed within a window
* @param window_length Window of time burst limit refers to
* @param array Pointer to array that stores record or byte count
* @param incValue Amount to increase array slot by
*
* @return true if record does not exceed threshold, and false otherwise
*/
bool rate_limit_check(int current_minute, int64_t burst_limit, int
window_length, size_t *array, size_t incValue);
/**
* Updates rate limit arrays with passing record information
*
* @param current_minute The current minute of time the record processed
* @param window_length Window of time burst limit refers to
* @param array Pointer to array that stores record or byte count
* @param incValue Amount to increase array slot by
*
*/
void rate_limit_update(int current_minute, int window_length,
size_t *array, size_t incValue);
/**
* Checks if the spool strategy is chosen for rate-limiting
*
* @param daemon Pointer to the daemon
*
* @return true if spool is choosen false if drop is choosen
*/
bool spool_strategy_selected(TelemDaemon *daemon);
/**
* Check if daemon should direct-spool after encountering network issues.
*
* @param daemon Pointer to the daemon
* @param time The time as time_t (usually from time())
*
* @return true if inside direct spool window, false otherwise.
*/
bool inside_direct_spool_window(TelemDaemon *daemon, time_t time);
/**
* Set the start time of the direct spool window. Will use the
* current time.
*
* @param daemon Pointer to the daemon
*
* @return no return value.
*/
void start_network_bypass(TelemDaemon *daemon);
/**
* Function pointer for posting the records to the server. Replaced by a
* mock function in the unit tests.
*
* @param header_values The header data read from the socket
* @param body The payload of the record read
* @param spool Indicate whether the record should be spooled if not
* successfully delivered
*
* @return true if the record was successfully posted
*/
extern bool (*post_record_ptr)(char *headers[], char *body, bool spool);
/**
* Post records on a http connection to server
*
* @param header_values The header data read from the socket
* @param body The payload of the record read
* @param spool Indicate whether the record should be spooled if not
* successfully delivered
*
* @return true if the record was successfully posted
*/
bool post_record_http(char *header_values[], char *body, bool spool);
/**
* Spool the record in case it cannot be delivered to the server
*
* @param headers The headers for the record
* @param body The payload of the record
*
*/
void spool_record(TelemDaemon * daemon, char *headers[], char *body);
/**
* Save a copy of the record payload locally
*
* @param body The payload of the record
*
*/
void save_local_copy(TelemDaemon *daemon, char *body);
/**
* Get random machine id stored in file
*
@@ -310,4 +167,5 @@ int update_machine_id(void);
* or if the file is empty
*/
char *read_machine_id_override(void);
/* vi: set ts=8 sw=8 sts=4 et tw=80 cino=(0: */
+5 -5
View File
@@ -316,7 +316,7 @@ static int set_system_build_header(struct telem_ref *t_ref)
/**
* Sets the header reporting the machine_id. Note, the machine_id is really
* managed by the telemetrics daemon (telemd), and populated by the daemon
* managed by the telemetrics daemon (telemprobd), and populated by the daemon
* as it delivers the records. Consequently, the header is simply set to a
* dummy value of 0xFFFFFFF.
*
@@ -410,7 +410,7 @@ static int set_cpu_model_header(struct telem_ref *t_ref)
model_str_len = strlen(model_name);
if (model_str_len > 2) {
model_name = (char *)model_name + 2 * sizeof(char);
model_name[model_str_len - 2] = '\0';
model_name[model_str_len - 3] = '\0';
} else {
model_name = "blank";
}
@@ -777,9 +777,9 @@ static int set_payload_format_header(struct telem_ref *t_ref, uint32_t payload_v
return status;
}
void tm_set_config_file(char *c_file)
int tm_set_config_file(const char *c_file)
{
set_config_file(c_file);
return set_config_file(c_file);
}
/**
@@ -1081,7 +1081,7 @@ int tm_set_event_id(struct telem_ref *t_ref, char *event_id)
}
/**
* Write nbytes from buf to fd. Used to send records to telemd.
* Write nbytes from buf to fd. Used to send records to telemprobd.
*
* @param fd Socket fd obtained from tm_get_socket.
* @param buf Data to be written to the socket.
+8 -5
View File
@@ -31,7 +31,10 @@
* - a library, libtelemetry, that telemetrics probes use to create telemetrics
* records and send them to the daemon for further processing.
*
* - a daemon, telemd, that prepares the records to send to a telemetrics server
* - telemprobd, a daemon that handles communication with probes and forwards
* records to a second daemon.
*
* - telempostd, this daemon sends telemetry records to a telemetrics server
* (not included in this source tree), or spools the records on disk in case
* it's unable to successfully deliver them.
*
@@ -55,11 +58,11 @@ struct telem_ref {
/**
* Set the configuration file name to use
*
* @param c_file Absolute file name of the configuration file. If the path name
* provided is not an absolute path, or the path given cannot be successfuly
* stat'd, then the effective configuration file is left unchanged.
* @param c_file Absolute file name of the configuration file.
* @return 0 on success, or a negative errno-style value on error. In case of
* an error return the effective configuration file is left unchanged.
*/
void tm_set_config_file(char *c_file);
int tm_set_config_file(const char *c_file);
/**
* Create a new telemetrics record
+755
View File
@@ -0,0 +1,755 @@
/*
* This program is part of the Clear Linux Project
*
* Copyright 2018 Intel Corporation
*
* This program is free software; you can redistribute it and/or modify it under
* the terms and conditions of the GNU Lesser General Public License, as
* published by the Free Software Foundation; either version 2.1 of the License,
* or (at your option) any later version.
*
* This program is distributed in the hope it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*/
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
#include <signal.h>
#include <dirent.h>
#include <malloc.h>
#include <stdbool.h>
#include <sys/stat.h>
#include <curl/curl.h>
#include <sys/signalfd.h>
#include "log.h"
#include "util.h"
#include "spool.h"
#include "iorecord.h"
#include "retention.h"
#include "telempostdaemon.h"
/* spool window check */
static bool inside_direct_spool_window(TelemPostDaemon *daemon, time_t current_time)
{
return (current_time < daemon->bypass_http_post_ts + NETWORK_BYPASS_DURATION) ? true : false;
}
/* set http bypass */
static void start_network_bypass(TelemPostDaemon *daemon)
{
daemon->bypass_http_post_ts = time(NULL);
}
/* burst limit check */
bool burst_limit_enabled(int64_t burst_limit)
{
return (burst_limit > -1) ? true : false;
}
/* rate limit check */
bool rate_limit_check(int current_minute, int64_t burst_limit, int
window_length, size_t *array, size_t incValue)
{
size_t count = 0;
/* Variable to determine last minute allowed in burst window */
int window_start = (TM_RATE_LIMIT_SLOTS + (current_minute - window_length + 1))
% TM_RATE_LIMIT_SLOTS;
/* The modulo is not placed in the for loop itself, because if
* the current minute is less than the window start (from wrapping)
* the for loop will never be entered.
*/
/* Counts all elements within burst_window in array */
for (int i = window_start; i < (window_start + window_length); i++) {
count += array[i % TM_RATE_LIMIT_SLOTS];
if ((array[i % TM_RATE_LIMIT_SLOTS] + incValue) >= SIZE_MAX) {
/* Exceeds maximum size array can hold */
return false;
}
}
/* Include current record being processed to count */
count += incValue;
/* Determines whether the count has exceeded the limit or not */
return (count > burst_limit) ? false : true;
}
/* spool strategy check */
bool spool_strategy_selected(TelemPostDaemon *daemon)
{
/* Performs action depending on strategy choosen */
return (strcmp(daemon->rate_limit_strategy, "spool") == 0) ? true : false;
}
/* rate limit check */
void rate_limit_update(int current_minute, int window_length, size_t *array,
size_t incValue)
{
/* Variable to determine the amount of zero'd out spots needed */
int blank_slots = (TM_RATE_LIMIT_SLOTS - window_length);
/* Update specified array depending on increment value */
array[current_minute] += incValue;
/* Zero out expired records for record array */
for (int i = current_minute + 1; i < (blank_slots + current_minute + 1); i++) {
array[i % TM_RATE_LIMIT_SLOTS] = 0;
}
}
static void set_pollfd(TelemPostDaemon *daemon, int fd, enum fdindex i, short events)
{
assert(daemon);
assert(fd);
daemon->pollfds[i].fd = fd;
daemon->pollfds[i].events = events;
daemon->pollfds[i].revents = 0;
}
static void initialize_signals(TelemPostDaemon *daemon)
{
int sigfd;
sigset_t mask;
sigemptyset(&mask);
if (sigaddset(&mask, SIGHUP) != 0) {
telem_perror("Error adding signal SIGHUP to mask");
exit(EXIT_FAILURE);
}
if (sigaddset(&mask, SIGINT) != 0) {
telem_perror("Error adding signal SIGINT to mask");
exit(EXIT_FAILURE);
}
if (sigaddset(&mask, SIGTERM) != 0) {
telem_perror("Error adding signal SIGTERM to mask");
exit(EXIT_FAILURE);
}
if (sigaddset(&mask, SIGPIPE) != 0) {
telem_perror("Error adding signal SIGPIPE to mask");
exit(EXIT_FAILURE);
}
if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1) {
telem_perror("Error changing signal mask with SIG_BLOCK");
exit(EXIT_FAILURE);
}
sigfd = signalfd(-1, &mask, 0);
if (sigfd == -1) {
telem_perror("Error creating the signalfd");
exit(EXIT_FAILURE);
}
daemon->sfd = sigfd;
set_pollfd(daemon, sigfd, signlfd, POLLIN);
}
static void initialize_rate_limit(TelemPostDaemon *daemon)
{
for (int i = 0; i < TM_RATE_LIMIT_SLOTS; i++) {
daemon->record_burst_array[i] = 0;
daemon->byte_burst_array[i] = 0;
}
daemon->rate_limit_enabled = rate_limit_enabled_config();
daemon->record_burst_limit = record_burst_limit_config();
daemon->record_window_length = record_window_length_config();
daemon->byte_burst_limit = byte_burst_limit_config();
daemon->byte_window_length = byte_window_length_config();
daemon->rate_limit_strategy = rate_limit_strategy_config();
}
static void initialize_record_delivery(TelemPostDaemon *daemon)
{
daemon->record_retention_enabled = record_retention_enabled_config();
daemon->record_server_delivery_enabled = record_server_delivery_enabled_config();
}
void initialize_daemon(TelemPostDaemon *daemon)
{
assert(daemon);
daemon->bypass_http_post_ts = 0;
daemon->is_spool_valid = is_spool_valid();
daemon->record_journal = open_journal(JOURNAL_PATH);
daemon->fd = inotify_init();
if (daemon->fd < 0) {
telem_perror("Error initializing inotify");
exit(EXIT_FAILURE);
}
daemon->wd = inotify_add_watch(daemon->fd, spool_dir_config(), IN_CLOSE_WRITE);
initialize_signals(daemon);
set_pollfd(daemon, daemon->fd, watchfd, POLLIN);
initialize_rate_limit(daemon);
initialize_record_delivery(daemon);
/* Register record retention delete action as a callback to prune entry */
if (daemon->record_journal != NULL && daemon->record_retention_enabled) {
daemon->record_journal->prune_entry_callback = &delete_record_by_id;
}
daemon->current_spool_size = 0;
}
size_t write_callback(char *ptr, size_t size, size_t nmemb, void *userdata)
{
telem_log(LOG_DEBUG, "Received data:\n%s\n", ptr);
return size * nmemb;
}
bool post_record_http(char *headers[], char *body)
{
CURL *curl;
int res = 0;
char *content = "Content-Type: application/text";
struct curl_slist *custom_headers = NULL;
char errorbuf[CURL_ERROR_SIZE];
long http_response = 0;
const char *cert_file = get_cainfo_config();
const char *tid_header = get_tidheader_config();
// 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"
" easy session, exiting\n");
exit(EXIT_FAILURE);
/* TODO: check if memory needs to be released */
}
// Errors for any curl_easy_* functions will store nice error messages
// in errorbuf, so send log messages with errorbuf contents
curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errorbuf);
curl_easy_setopt(curl, CURLOPT_URL, server_addr_config());
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 5L);
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 10L);
curl_easy_setopt(curl, CURLOPT_POST, 1);
#ifdef DEBUG
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
#endif
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);
for (int i = 0; i < NUM_HEADERS; i++) {
custom_headers = curl_slist_append(custom_headers, headers[i]);
}
custom_headers = curl_slist_append(custom_headers, tid_header);
// This should be set by probes/libtelemetry in the future
custom_headers = curl_slist_append(custom_headers, content);
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, custom_headers);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, body);
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, strlen(body));
curl_easy_setopt(curl, CURLOPT_USE_SSL, CURLUSESSL_TRY);
if (strlen(cert_file) > 0) {
if (access(cert_file, F_OK) != -1) {
curl_easy_setopt(curl, CURLOPT_CAINFO, cert_file);
telem_log(LOG_INFO, "cafile was set to %s\n", cert_file);
}
}
telem_log(LOG_DEBUG, "Executing curl operation...\n");
errorbuf[0] = 0;
res = curl_easy_perform(curl);
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_response);
if (res) {
size_t len = strlen(errorbuf);
if (len) {
telem_log(LOG_DEBUG, "Failed sending record: %s%s", errorbuf,
((errorbuf[len - 1] != '\n') ? "\n" : ""));
} else {
telem_log(LOG_DEBUG, "Failed sending record: %s\n",
curl_easy_strerror(res));
}
} else if (http_response != 201 && http_response != 200) {
/* 201 means the record was successfully created
* 200 is a generic "ok".
*/
telem_log(LOG_ERR, "Encountered error %ld on the server\n",
http_response);
// We treat HTTP error codes the same as libcurl errors
res = 1;
} else {
telem_log(LOG_INFO, "Record sent successfully\n");
}
curl_slist_free_all(custom_headers);
curl_easy_cleanup(curl);
curl_global_cleanup();
return res ? false : true;
}
static void save_local_copy(TelemPostDaemon *daemon, char *body)
{
int ret = 0;
char *tmpbuf = NULL;
FILE *tmpfile = NULL;
if (daemon == NULL || daemon->record_journal == NULL ||
daemon->record_journal->latest_record_id == NULL) {
return;
}
ret = asprintf(&tmpbuf, "%s/%s", RECORD_RETENTION_DIR,
daemon->record_journal->latest_record_id);
if (ret == -1) {
telem_log(LOG_ERR, "Failed to allocate memory for record full path, aborting\n");
return;
}
tmpfile = fopen(tmpbuf, "w");
if (!tmpfile) {
telem_perror("Error opening local record copy temp file");
goto save_err;
}
// Save body
fprintf(tmpfile, "%s\n", body);
fclose(tmpfile);
save_err:
free(tmpbuf);
return;
}
static void save_entry_to_journal(TelemPostDaemon *daemon, time_t t_stamp, char *headers[])
{
char *classification_value;
char *event_id_value;
if (get_header_value(headers[TM_CLASSIFICATION], &classification_value) &&
get_header_value(headers[TM_EVENT_ID], &event_id_value)) {
if (new_journal_entry(daemon->record_journal, classification_value, t_stamp, event_id_value) != 0) {
telem_log(LOG_INFO, "new_journal_entry in process_record: failed saving record entry\n");
}
}
free(classification_value);
free(event_id_value);
}
/* Check window length conf values */
static bool windows_length_value_check(TelemPostDaemon *daemon)
{
if (daemon->record_window_length == -1 ||
daemon->byte_window_length == -1) {
telem_log(LOG_ERR, "Invalid value for window length\n");
return false;
}
return true;
}
/* Rate limiting checks */
static void rate_limit_checks(TelemPostDaemon *daemon, bool *record_check_passed, bool *byte_check_passed)
{
time_t temp = time(NULL);
struct tm *tm_s = localtime(&temp);
int current_minute;
current_minute = tm_s->tm_min;
bool record_burst_enabled = burst_limit_enabled(daemon->record_burst_limit);
bool byte_burst_enabled = burst_limit_enabled(daemon->byte_burst_limit);
/* Checks if entirety of rate limiting is enabled */
if (daemon->rate_limit_enabled) {
/* Checks whether record and byte bursts are enabled individually */
record_burst_enabled = burst_limit_enabled(daemon->record_burst_limit);
byte_burst_enabled = burst_limit_enabled(daemon->byte_burst_limit);
if (record_burst_enabled) {
*record_check_passed = rate_limit_check(current_minute,
daemon->record_burst_limit, daemon->record_window_length,
daemon->record_burst_array, TM_RECORD_COUNTER);
}
if (byte_burst_enabled) {
*byte_check_passed = rate_limit_check(current_minute,
daemon->byte_burst_limit, daemon->byte_window_length,
daemon->byte_burst_array, RECORD_SIZE_LEN);
}
/* If both record and byte burst disabled, rate limiting disabled */
if (!record_burst_enabled && !byte_burst_enabled) {
daemon->rate_limit_enabled = false;
}
}
}
/* Wrapper for save local copy */
static void apply_retention_policies(TelemPostDaemon *daemon, char *body)
{
if (daemon->record_retention_enabled) {
save_local_copy(daemon, body);
}
}
/* Deliver record to backend if rate limiting policies are met otherwise
* spool record for future delivery */
static bool deliver_record(TelemPostDaemon *daemon, char *headers[], char *body)
{
bool ret = false;
int current_minute;
bool do_spool = false;
bool record_sent = false;
time_t temp = time(NULL);
struct tm *tm_s = localtime(&temp);
current_minute = tm_s->tm_min;
/* Checks flags */
bool record_check_passed = true;
bool byte_check_passed = true;
bool record_burst_enabled = burst_limit_enabled(daemon->record_burst_limit);
bool byte_burst_enabled = burst_limit_enabled(daemon->byte_burst_limit);
/* Perform record and byte rate limiting checks */
rate_limit_checks(daemon, &record_check_passed, &byte_check_passed);
/* Sends record if rate limiting is disabled, or all checks passed */
if (!daemon->rate_limit_enabled || (record_check_passed && byte_check_passed)) {
/* Send the record as https post */
record_sent = post_record_ptr(headers, body);
/**
* This is the only point where an error condition could be returned
* if the record was not sent
* */
ret = record_sent;
}
// Get rate-limit strategy
do_spool = spool_strategy_selected(daemon);
// Drop record
if (!record_sent && !do_spool) {
// Not an error condition
ret = true;
}
// Spool Record
else if (!record_sent && do_spool) {
start_network_bypass(daemon);
telem_log(LOG_INFO, "process_record: initializing direct-spool window\n");
// False will keep record around
ret = false;
} else {
/* Updates rate limiting arrays if record sent */
if (record_burst_enabled) {
rate_limit_update(current_minute, daemon->record_window_length,
daemon->record_burst_array, TM_RECORD_COUNTER);
}
if (byte_burst_enabled) {
rate_limit_update(current_minute, daemon->byte_window_length,
daemon->byte_burst_array, RECORD_SIZE_LEN);
}
}
return ret;
}
bool process_staged_record(char *filename, bool is_retry, TelemPostDaemon *daemon)
{
int k;
bool ret = false;
char *headers[NUM_HEADERS];
char *body = NULL;
struct stat buf;
time_t current_time = time(NULL);
int64_t max_spool_size = 0;
for (k = 0; k < NUM_HEADERS; k++) {
headers[k] = NULL;
}
/** Load record **/
if ((ret = read_record(filename, headers, &body)) == false) {
telem_log(LOG_WARNING, "unable to read record\n");
ret = true; // Record corrupted? true will remove record
goto end_processing_file;
}
/** Get file information **/
if (stat(filename, &buf) == -1) {
telem_perror("Processing staged file unable to stat record in spool");
ret = true; // true to remove it
goto end_processing_file;
}
/** Update spool directory size **/
(daemon->current_spool_size) += (buf.st_blocks * 512);
/** Check that record is not expired **/
if (!S_ISREG(buf.st_mode) ||
(current_time - buf.st_mtime > (record_expiry_config() * 60)) ||
(buf.st_uid != getuid())) {
ret = true; // Expired, true to remove it
goto end_processing_file;
}
/* Retries should not be recorded */
if (is_retry == false) {
/** Journal entry **/
save_entry_to_journal(daemon, current_time, headers);
/** Record retention **/
apply_retention_policies(daemon, body);
}
/** Record delivery **/
if (!daemon->record_server_delivery_enabled) {
#ifdef DEBUG
telem_log(LOG_WARNING, "record server delivery disabled\n");
#endif
// Not an error condition
ret = true;
goto end_processing_file;
}
/** Spool policies **/
if (inside_direct_spool_window(daemon, time(NULL))) {
#ifdef DEBUG
telem_log(LOG_INFO, "process_record: delivering directly to spool\n");
#endif
/* Check spool max size conf */
max_spool_size = spool_max_size_config();
if (max_spool_size != -1 &&
daemon->current_spool_size >= (max_spool_size * 1024)) {
// Drop record
telem_log(LOG_INFO, "Spool dir full, dropping record\n");
ret = true;
} else {
// Keep record, non error condition
ret = false;
}
goto end_processing_file;
}
/** Check window_length **/
if (windows_length_value_check(daemon) == false) {
exit(EXIT_FAILURE);
}
/** Deliver or spool **/
ret = deliver_record(daemon, headers, body);
end_processing_file:
/** Update spool size if record will be removed **/
if (ret) {
(daemon->current_spool_size) -= (buf.st_blocks * 512);
}
telem_log(LOG_DEBUG, "spool_size: %ld\n", daemon->current_spool_size);
free(body);
for (k = 0; k < NUM_HEADERS; k++) {
free(headers[k]);
}
return ret;
}
static int directory_dot_filter(const struct dirent *entry)
{
if ((strcmp(entry->d_name, ".") == 0) || (strcmp(entry->d_name, "..") == 0)) {
return 0;
} else {
return 1;
}
}
int staging_records_loop(TelemPostDaemon *daemon)
{
int ret;
int processed;
int numentries;
struct dirent **namelist;
numentries = scandir(spool_dir_config(), &namelist, directory_dot_filter, NULL);
processed = 0;
if (numentries == 0) {
telem_log(LOG_DEBUG, "No entries in staging\n");
return numentries;
} else if (numentries < 0) {
telem_perror("Error while scanning staging");
return numentries;
}
for (int i = 0; i < numentries; i++) {
char *record_path;
telem_log(LOG_DEBUG, "Processing staged record: %s\n",
namelist[i]->d_name);
ret = asprintf(&record_path, "%s/%s", spool_dir_config(), namelist[i]->d_name);
if (ret == -1) {
telem_log(LOG_ERR, "Failed to allocate memory for staging record full path\n");
exit(EXIT_FAILURE);
}
if (process_staged_record(record_path, true, daemon)) {
unlink(record_path);
processed++;
}
free(record_path);
}
for (int i = 0; i < numentries; i++) {
free(namelist[i]);
}
free(namelist);
return numentries - processed;
}
void run_daemon(TelemPostDaemon *daemon)
{
int ret;
int retry_attempt = MAX_RETRY_ATTEMPTS;
int spool_process_time = spool_process_time_config();
bool daemon_recycling_enabled = daemon_recycling_enabled_config();
time_t last_spool_run_time = time(NULL);
time_t last_record_received = time(NULL);
assert(daemon);
assert(daemon->pollfds);
assert(daemon->pollfds[signlfd].fd);
assert(daemon->pollfds[watchfd].fd);
/* Post at boot failed initialize retries variable */
if (daemon->bypass_http_post_ts != 0) {
retry_attempt = 1;
}
while (1) {
int retry_delay = spool_process_time;
malloc_trim(0);
if (retry_attempt < MAX_RETRY_ATTEMPTS) {
retry_delay = retry_attempt * retry_attempt;
daemon->bypass_http_post_ts = 0;
telem_log(LOG_INFO, "Record delivery failed will retry in %d seconds",
retry_delay);
}
ret = poll(daemon->pollfds, NFDS, retry_delay * 1000);
if (ret == -1) {
telem_perror("Failed to poll daemon file descriptors");
break;
} else if (ret != 0) {
if (daemon->pollfds[signlfd].revents != 0) {
struct signalfd_siginfo fdsi;
ssize_t s;
s = read(daemon->sfd, &fdsi, sizeof(struct signalfd_siginfo));
if (s != sizeof(struct signalfd_siginfo)) {
telem_perror("Error while reading from the signal"
"file descriptor");
exit(EXIT_FAILURE);
}
if (fdsi.ssi_signo == SIGTERM || fdsi.ssi_signo == SIGINT) {
telem_log(LOG_INFO, "Received either a \
SIGINT/SIGTERM signal\n");
break;
}
} else if (daemon->pollfds[watchfd].revents != 0) {
int ret = 0;
ssize_t i = 0;
ssize_t length = 0;
char buffer[BUFFER_LEN];
length = read(daemon->fd, buffer, BUFFER_LEN);
if (length < 0) {
telem_perror("Error while reading from inotify"
"watcher");
exit(EXIT_FAILURE);
}
while (i < length) {
struct inotify_event *event = (struct inotify_event *)&buffer[i];
if (event->len) {
if (event->mask & IN_CLOSE_WRITE && !(event->mask & IN_ISDIR)) {
char *record_name = NULL;
/* Retrieve foldername from watch id? */
ret = asprintf(&record_name, "%s/%s", spool_dir_config(), event->name);
if (ret == -1) {
telem_log(LOG_ERR, "Failed to allocate memory for record full path, aborting\n");
exit(EXIT_FAILURE);
}
/* Process inotify event */
if (process_staged_record(record_name, false, daemon)) {
unlink(record_name);
}
free(record_name);
last_record_received = time(NULL);
}
}
i += (ssize_t)EVENT_SIZE + event->len;
}
}
} else {
time_t now = time(NULL);
/* time to recycle the daemon has elapsed*/
if (daemon_recycling_enabled &&
difftime(now, last_record_received) >= TM_DAEMON_EXIT_TIME) {
/* Exit */
telem_log(LOG_INFO, "Telemetry post daemon exiting for recycling\n");
break;
}
/* Check if this is a retry */
if (retry_attempt < MAX_RETRY_ATTEMPTS) {
if (staging_records_loop(daemon) == 0) {
retry_attempt = MAX_RETRY_ATTEMPTS + 1;
} else {
retry_attempt++;
}
} else if (retry_attempt == MAX_RETRY_ATTEMPTS) {
retry_attempt = MAX_RETRY_ATTEMPTS + 1;
telem_log(LOG_ERR, "Record deliver failed after %d attempts",
MAX_RETRY_ATTEMPTS);
}
/* Check spool */
if (difftime(now, last_spool_run_time) >= spool_process_time) {
spool_records_loop(&(daemon->current_spool_size));
last_spool_run_time = time(NULL);
}
}
/* Check journal records and prune if needed */
ret = prune_journal(daemon->record_journal, JOURNAL_TMPDIR);
if (ret != 0) {
telem_log(LOG_WARNING, "Unable to prune journal\n");
}
}
}
void close_daemon(TelemPostDaemon *daemon)
{
if (daemon->fd) {
if (daemon->wd) {
inotify_rm_watch(daemon->fd, daemon->wd);
}
close(daemon->fd);
}
close_journal(daemon->record_journal);
}
/* vi: set ts=8 sw=8 sts=4 et tw=80 cino=(0: */
+135
View File
@@ -0,0 +1,135 @@
/*
* This program is part of the Clear Linux Project
*
* Copyright 2018 Intel Corporation
*
* This program is free software; you can redistribute it and/or modify it under
* the terms and conditions of the GNU Lesser General Public License, as
* published by the Free Software Foundation; either version 2.1 of the License,
* or (at your option) any later version.
*
* This program is distributed in the hope it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*/
#define EVENT_SIZE sizeof(struct inotify_event)
#define BUFFER_LEN 1024 * (EVENT_SIZE + 16)
#define NFDS 2
#define TM_RATE_LIMIT_SLOTS (1 /*h*/ * 60 /*m*/)
#define TM_RECORD_COUNTER (1)
#define MAX_RETRY_ATTEMPTS 8
#define NETWORK_BYPASS_DURATION TM_DAEMON_EXIT_TIME
#include <poll.h>
#include <stdbool.h>
#include <sys/inotify.h>
#include "common.h"
#include "journal/journal.h"
#include "configuration.h"
enum fdindex {signlfd, watchfd};
typedef struct TelemPostDaemon {
int fd;
int wd;
int sfd;
char event_buffer[BUFFER_LEN];
struct pollfd pollfds[NFDS];
/* Telemetry Journal*/
TelemJournal *record_journal;
/* Time of last failed post */
time_t bypass_http_post_ts;
/* Rate limit record and byte arrays */
size_t record_burst_array[TM_RATE_LIMIT_SLOTS];
size_t byte_burst_array[TM_RATE_LIMIT_SLOTS];
/* Rate Limit Configurations */
bool rate_limit_enabled;
int64_t record_burst_limit;
int record_window_length;
int64_t byte_burst_limit;
int byte_window_length;
const char *rate_limit_strategy;
/* Spool configuration */
bool is_spool_valid;
long current_spool_size;
/* Record local copy and delivery */
bool record_retention_enabled;
bool record_server_delivery_enabled;
} TelemPostDaemon;
/**
* Initializes daemon
*
* @param daemon a pointer to telemetry post daemon
*/
void initialize_daemon(TelemPostDaemon *daemon);
/**
* Starts daemon
*
* @param daemon a pointer to telemetry post daemon
*/
void run_daemon(TelemPostDaemon *daemon);
/**
* Cleans up inotify descriptors
*
* @param daemon a pointer to telemetry post daemon
*/
void close_daemon(TelemPostDaemon *daemon);
/**
* Processed record written on disk
*
* @param filename a pointor to record on disk
* @param is_retry a boolean value that indicates if
* the record has been previously processed.
* @param daemon post to telemetry post daemon
*/
bool process_staged_record(char *filename, bool is_retry, TelemPostDaemon *daemon);
/**
* Scans staging directory to process files that were
* missed by file watcher
*
* @param daemon a pointer to telemetry post daemon
* @return the number of records that were removed from spool
*/
int staging_records_loop(TelemPostDaemon *daemon);
/**
* Posts a record to backend
*
* @param headers a pointer to an array with keys and values
* @param body a pointer to the payload
*/
bool post_record_http(char *headers[], char *body);
/**
* Pointer to function to isolate backend call during
* unit testing.
*
* @param headers pointer to array of keys
* @param body a pinter to payload
* */
extern bool (*post_record_ptr)(char *headers[], char *body);
/** Helper functions **/
/* rate limit check */
bool rate_limit_check(int current_minute, int64_t burst_limit,
int window_length, size_t *array, size_t incValue);
/* burst limit check */
bool burst_limit_enabled(int64_t burst_limit);
/* rate limit check */
void rate_limit_update(int current_minute, int window_length, size_t *array,
size_t incValue);
/* spool strategy check */
bool spool_strategy_selected(TelemPostDaemon *daemon);
/* vi: set ts=8 sw=8 sts=4 et tw=80 cino=(0: */
+3 -1
View File
@@ -34,8 +34,10 @@
#include "common.h"
#include "util.h"
bool get_header(const char *haystack, const char *needle, char **line, size_t len)
bool get_header(const char *haystack, const char *needle, char **line)
{
size_t len = strlen(needle);
if (haystack && !strncmp(haystack, needle, len)) {
*line = strdup(haystack);
return true;
+1 -1
View File
@@ -22,7 +22,7 @@
void *reallocate(void **addr, size_t *allocated, size_t requested);
/* Check if haystacks begins with needle and return a copy of haystack */
bool get_header(const char *haystack, const char *needle, char **line, size_t len);
bool get_header(const char *haystack, const char *needle, char **line);
/* Get the value of a header */
bool get_header_value(const char *header, char **value);
+2 -2
View File
@@ -40,7 +40,7 @@ START_TEST(check_read_valid_config)
ck_assert_str_eq(config.strValues[CONF_SPOOL_DIR], "/tmp/spool");
ck_assert_int_eq(config.intValues[CONF_RECORD_EXPIRY], 1200);
ck_assert_int_eq(config.intValues[CONF_SPOOL_MAX_SIZE], 1024);
ck_assert_int_eq(config.intValues[CONF_SPOOL_PROCESS_TIME], 900);
ck_assert_int_eq(config.intValues[CONF_SPOOL_PROCESS_TIME], 180);
ck_assert_int_eq(config.intValues[CONF_RECORD_BURST_LIMIT], 100);
ck_assert_int_eq(config.intValues[CONF_RECORD_WINDOW_LENGTH], 15);
ck_assert_int_eq(config.intValues[CONF_BYTE_BURST_LIMIT], 1000);
@@ -94,7 +94,7 @@ START_TEST(check_config_initialised)
ck_assert_str_eq(spool_dir_config(), "/tmp/spool");
ck_assert_int_eq(record_expiry_config(), 1200);
ck_assert_int_eq(spool_max_size_config(), 1024);
ck_assert_int_eq(spool_process_time_config(), 900);
ck_assert_int_eq(spool_process_time_config(), 180);
ck_assert_int_eq(record_burst_limit_config(), 100);
ck_assert_int_eq(record_window_length_config(), 15);
ck_assert_int_eq(byte_burst_limit_config(), 1000);
+2 -1
View File
@@ -26,7 +26,8 @@ static struct TelemJournal *journal = NULL;
static char *eid = "00007766547776eb7fc478eb0eb43e43";
static int K = 20;
void teardown(void) {
void teardown(void)
{
remove(journal_file);
}
-1
View File
@@ -190,4 +190,3 @@ int main(void)
}
/* vi: set ts=8 sw=8 sts=4 et tw=80 cino=(0: */
+443
View File
@@ -0,0 +1,443 @@
/*
* This program is part of the Clear Linux Project
*
* Copyright 2018 Intel Corporation
*
* This program is free software; you can redistribute it and/or modify it under
* the terms and conditions of the GNU Lesser General Public License, as
* published by the Free Software Foundation; either version 2.1 of the License,
* or (at your option) any later version.
*
* This program is distributed in the hope it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*/
#include <check.h>
#include <sys/socket.h>
#include <sys/fcntl.h>
#include <stdlib.h>
#include <sys/queue.h>
#include <unistd.h>
#include "configuration.h"
#include "telempostdaemon.h"
#include "common.h"
TelemPostDaemon tdaemon;
bool dummy_post(char *headers[], char *body)
{
return true;
}
bool (*post_record_ptr)(char *headers[], char *body) = dummy_post;
void setup(void)
{
char *config_file = ABSTOPSRCDIR "/src/data/example.conf";
set_config_file(config_file);
initialize_daemon(&tdaemon);
}
START_TEST(check_daemon_is_initialized)
{
setup();
ck_assert(tdaemon.rate_limit_enabled == true);
ck_assert(tdaemon.record_burst_limit == 100);
ck_assert(tdaemon.record_window_length == 15);
ck_assert(tdaemon.byte_burst_limit == 1000);
ck_assert(tdaemon.byte_window_length == 20);
ck_assert_str_eq(tdaemon.rate_limit_strategy, "spool");
}
END_TEST
START_TEST(check_handle_client_with_no_data)
{
setup();
bool success;
char *filename = ABSTOPSRCDIR "/tests/telempostd/empty_message";
success = process_staged_record(filename, false, &tdaemon);
// Return true to remove corrupted record
ck_assert(success == true);
}
END_TEST
START_TEST(check_handle_client_with_incorrect_data)
{
setup();
bool success;
char *filename = ABSTOPSRCDIR "/tests/telempostd/incorrect_message";
success = process_staged_record(filename, false, &tdaemon);
// Return true to remove corrupted record
ck_assert(success == true);
}
END_TEST
START_TEST(check_process_record_with_correct_size_and_data)
{
setup();
bool success;
char *filename = ABSTOPSRCDIR "/tests/telempostd/correct_message";
success = process_staged_record(filename, false, &tdaemon);
ck_assert(success == true);
}
END_TEST
START_TEST(check_process_record_with_incorrect_headers)
{
setup();
bool success;
char *filename = ABSTOPSRCDIR "/tests/telempostd/incorrect_headers";
success = process_staged_record(filename, false, &tdaemon);
// Return true to remove corrupted record
ck_assert(success == true);
}
END_TEST
START_TEST(check_rate_limit_enabled_functions)
{
setup();
bool record_check_passed = true;
bool byte_check_passed = true;
bool record_burst_enabled = true;
bool byte_burst_enabled = true;
tdaemon.record_burst_limit = 4;
tdaemon.byte_burst_limit = 2999;
tdaemon.rate_limit_enabled = true;
bool burst_limit_enabled(int64_t burst_limit)
{
return (burst_limit > -1) ? true : false;
}
/* Checks whether record burst rate limiting is enabled */
if (burst_limit_enabled(tdaemon.record_burst_limit)) {
record_check_passed = false;
} else {
/* If not enabled, does not need to be checked */
record_burst_enabled = false;
}
/* Checks whether byte burst rate limiting is enabled */
if (burst_limit_enabled(tdaemon.byte_burst_limit)) {
byte_check_passed = false;
} else {
/* If not enabled, does not need to be checked */
byte_burst_enabled = false;
}
if (!record_burst_enabled && !byte_burst_enabled) {
tdaemon.rate_limit_enabled = false;
}
ck_assert(record_check_passed == false);
ck_assert(byte_check_passed == false);
ck_assert(tdaemon.rate_limit_enabled == true);
}
END_TEST
START_TEST(check_rate_limit_records_that_pass)
{
setup();
int current_minute = 3;
tdaemon.record_burst_array[1] = 10;
tdaemon.record_burst_array[7] = 10;
tdaemon.record_burst_array[10] = 20;
tdaemon.record_burst_array[55] = 10;
tdaemon.record_burst_limit = 30;
tdaemon.record_window_length = 15;
bool checker;
checker = rate_limit_check(current_minute, tdaemon.record_burst_limit,
tdaemon.record_window_length, tdaemon.record_burst_array, 1);
ck_assert(checker == true);
}
END_TEST
START_TEST(check_rate_limit_records_that_do_not_pass)
{
setup();
int current_minute = 24;
tdaemon.record_burst_array[3] = 10;
tdaemon.record_burst_array[4] = 10;
tdaemon.record_burst_array[6] = 10;
tdaemon.record_burst_array[27] = 10;
tdaemon.record_burst_limit = 30;
tdaemon.record_window_length = 30;
bool checker;
checker = rate_limit_check(current_minute, tdaemon.record_burst_limit,
tdaemon.record_window_length, tdaemon.record_burst_array, 1);
ck_assert(checker == false);
}
END_TEST
START_TEST(check_rate_limit_bytes_that_pass)
{
setup();
size_t incValue = 20000;
int current_minute = 3;
tdaemon.byte_burst_array[1] = 32000;
tdaemon.byte_burst_array[7] = 10000;
tdaemon.byte_burst_array[10] = 45000;
tdaemon.byte_burst_array[55] = 11000;
tdaemon.byte_burst_limit = 64000;
tdaemon.byte_window_length = 15;
bool checker;
checker = rate_limit_check(current_minute, tdaemon.byte_burst_limit,
tdaemon.byte_window_length, tdaemon.byte_burst_array, incValue);
ck_assert(checker == true);
}
END_TEST
START_TEST(check_rate_limit_bytes_that_do_not_pass)
{
setup();
size_t incValue = 80000;
int current_minute = 15;
tdaemon.byte_burst_array[1] = 32000;
tdaemon.byte_burst_array[7] = 10000;
tdaemon.byte_burst_array[10] = 32300;
tdaemon.byte_burst_array[55] = 31000;
tdaemon.byte_burst_limit = 100000;
tdaemon.byte_window_length = 15;
bool checker;
checker = rate_limit_check(current_minute, tdaemon.byte_burst_limit,
tdaemon.byte_window_length, tdaemon.byte_burst_array, incValue);
ck_assert(checker == false);
}
END_TEST
START_TEST(check_update_record_array)
{
setup();
int current_minute = 7;
tdaemon.record_window_length = 15;
rate_limit_update(current_minute, tdaemon.record_window_length,
tdaemon.record_burst_array, TM_RECORD_COUNTER);
rate_limit_update(current_minute, tdaemon.record_window_length,
tdaemon.record_burst_array, TM_RECORD_COUNTER);
current_minute = 45;
rate_limit_update(current_minute, tdaemon.record_window_length,
tdaemon.record_burst_array, TM_RECORD_COUNTER);
current_minute = 47;
rate_limit_update(current_minute, tdaemon.record_window_length,
tdaemon.record_burst_array, TM_RECORD_COUNTER);
rate_limit_update(current_minute, tdaemon.record_window_length,
tdaemon.record_burst_array, TM_RECORD_COUNTER);
for (int i = 0; i < 60; i++) {
if (i != 45 && i != 47) {
ck_assert_msg(tdaemon.record_burst_array[i] == 0, "array at %d is %d\n",
i, tdaemon.record_burst_array[i]);
} else {
ck_assert_msg(tdaemon.record_burst_array[45] == 1, "array: %d is %d\n",
i, tdaemon.record_burst_array[i]);
ck_assert_msg(tdaemon.record_burst_array[47] == 2, "array: %d is %d\n",
i, tdaemon.record_burst_array[i]);
}
}
}
END_TEST
START_TEST(check_update_byte_array)
{
setup();
int current_minute = 7;
tdaemon.byte_window_length = 15;
size_t record_size = 32000;
rate_limit_update(current_minute, tdaemon.byte_window_length,
tdaemon.byte_burst_array, record_size);
rate_limit_update(current_minute, tdaemon.byte_window_length,
tdaemon.byte_burst_array, record_size);
current_minute = 45;
rate_limit_update(current_minute, tdaemon.byte_window_length,
tdaemon.byte_burst_array, record_size);
current_minute = 47;
rate_limit_update(current_minute, tdaemon.byte_window_length,
tdaemon.byte_burst_array, record_size);
rate_limit_update(current_minute, tdaemon.byte_window_length,
tdaemon.byte_burst_array, record_size);
rate_limit_update(current_minute, tdaemon.byte_window_length,
tdaemon.byte_burst_array, record_size);
for (int i = 0; i < 60; i++) {
if (i != 45 && i != 47) {
ck_assert_msg(tdaemon.byte_burst_array[i] == 0, "array at %d is %d\n",
i, tdaemon.record_burst_array[i]);
} else {
ck_assert_msg(tdaemon.byte_burst_array[45] == 32000, "bytearray: %d is %d\n",
i, tdaemon.record_burst_array[i]);
ck_assert_msg(tdaemon.byte_burst_array[47] == 96000, "bytearray: %d is %d\n",
i, tdaemon.record_burst_array[i]);
}
}
}
END_TEST
START_TEST(check_strategy_spool_option)
{
setup();
tdaemon.rate_limit_strategy = "spool";
bool do_spool = true;
char *ret = NULL;
bool record_sent = false;
// Get rate-limit strategy
do_spool = spool_strategy_selected(&tdaemon);
// Drop record
if (!record_sent && !do_spool) {
ret = "dropped";
}
// Spool Record
else if (!record_sent && do_spool) {
ret = "spooled";
} else {
ret = "sent";
}
ck_assert_str_eq(ret, "spooled");
}
END_TEST
START_TEST(check_strategy_drop_option)
{
setup();
tdaemon.rate_limit_strategy = "drop";
bool do_spool = true;
char *ret = NULL;
bool record_sent = false;
// Get rate-limit strategy
do_spool = spool_strategy_selected(&tdaemon);
// Drop record
if (!record_sent && !do_spool) {
ret = "dropped";
}
// Spool Record
else if (!record_sent && do_spool) {
ret = "spooled";
} else {
ret = "sent";
}
ck_assert_str_eq(ret, "dropped");
}
END_TEST
START_TEST(check_strategy_if_record_sent)
{
setup();
tdaemon.rate_limit_strategy = "drop";
bool do_spool = true;
char *ret = NULL;
bool record_sent = true;
// Get rate-limit strategy
do_spool = spool_strategy_selected(&tdaemon);
// Drop record
if (!record_sent && !do_spool) {
ret = "dropped";
}
// Spool Record
else if (!record_sent && do_spool) {
ret = "spooled";
} else {
ret = "sent";
}
ck_assert_str_eq(ret, "sent");
}
END_TEST
Suite *config_suite(void)
{
// A suite is comprised of test cases, defined below
Suite *s = suite_create("postd");
// Individual unit tests are added to "test cases"
TCase *t = tcase_create("postd");
tcase_add_test(t, check_daemon_is_initialized);
tcase_add_test(t, check_handle_client_with_no_data);
tcase_add_test(t, check_handle_client_with_incorrect_data);
tcase_add_test(t, check_process_record_with_correct_size_and_data);
tcase_add_test(t, check_process_record_with_incorrect_headers);
tcase_add_test(t, check_rate_limit_enabled_functions);
tcase_add_test(t, check_rate_limit_records_that_pass);
tcase_add_test(t, check_rate_limit_records_that_do_not_pass);
tcase_add_test(t, check_rate_limit_bytes_that_pass);
tcase_add_test(t, check_rate_limit_bytes_that_do_not_pass);
tcase_add_test(t, check_update_record_array);
tcase_add_test(t, check_update_byte_array);
tcase_add_test(t, check_strategy_spool_option);
tcase_add_test(t, check_strategy_drop_option);
tcase_add_test(t, check_strategy_if_record_sent);
suite_add_tcase(s, t);
return s;
}
int main(void)
{
Suite *s;
SRunner *sr;
s = config_suite();
sr = srunner_create(s);
// Use the TAP driver for now, so that each
// unit test will PASS/FAIL in the log output.
srunner_set_log(sr, NULL);
srunner_set_tap(sr, "-");
srunner_run_all(sr, CK_SILENT);
// failed = srunner_ntests_failed(sr);
srunner_free(sr);
// if you want the TAP driver to report a hard error based
// on certain conditions (e.g. number of failed tests, etc.),
// return non-zero here instead.
return 0;
}
/* vi: set ts=8 sw=8 sts=4 et tw=80 cino=(0: */
+2 -316
View File
@@ -26,7 +26,6 @@
#include "common.h"
TelemDaemon tdaemon;
static bool post_was_called = false;
char *get_serialized_record(char *headers, char *post_body, size_t *record_size)
{
@@ -54,14 +53,6 @@ char *get_serialized_record(char *headers, char *post_body, size_t *record_size)
return data;
}
bool dummy_post(char *headers[], char *body, bool spool)
{
post_was_called = true;
return post_was_called;
}
bool (*post_record_ptr)(char *[], char *, bool) = dummy_post;
void setup(void)
{
char *config_file = ABSTOPSRCDIR "/src/data/example.conf";
@@ -76,12 +67,6 @@ START_TEST(check_daemon_is_initialized)
ck_assert(tdaemon.nfds == 0);
ck_assert(tdaemon.pollfds == NULL);
ck_assert(tdaemon.rate_limit_enabled == true);
ck_assert(tdaemon.record_burst_limit == 100);
ck_assert(tdaemon.record_window_length == 15);
ck_assert(tdaemon.byte_burst_limit == 1000);
ck_assert(tdaemon.byte_window_length == 20);
ck_assert_str_eq(tdaemon.rate_limit_strategy, "spool");
}
END_TEST
@@ -269,12 +254,10 @@ START_TEST(check_handle_client_with_correct_size)
memcpy(buf + 2 * sizeof(uint32_t), data, strlen(data) + 1);
ck_assert(strcmp(data, buf + 2 * sizeof(int32_t)) == 0);
post_was_called = false;
ssize_t ret = write(server_fd, buf, 2 * sizeof(uint32_t) + size + 1);
ck_assert(ret != -1);
processed = handle_client(&tdaemon, 0, cl);
ck_assert(processed == true);
ck_assert(post_was_called == false);
ck_assert_msg(is_client_list_empty(&(tdaemon.client_head)), "Failed to remove client with no data\n");
ck_assert_msg(tdaemon.nfds == 0, "Failed to remove poll fd for client with n data\n");
@@ -307,14 +290,12 @@ START_TEST(check_process_record_with_correct_size_and_data)
ck_assert_msg(cl != NULL, "failed to malloc client");
add_pollfd(&tdaemon, client_fd, POLLIN | POLLPRI);
post_was_called = false;
record = get_serialized_record(headers, post_body, &record_size);
ssize_t ret = write(server_fd, record, record_size);
ck_assert(ret == record_size);
processed = handle_client(&tdaemon, 0, cl);
ck_assert(processed == true);
ck_assert(post_was_called == true);
ck_assert_msg(is_client_list_empty(&(tdaemon.client_head)), "Failed to remove client with correct data\n");
ck_assert_msg(tdaemon.nfds == 0, "Failed to remove poll fd for client with correct data\n");
close(server_fd);
@@ -341,14 +322,12 @@ START_TEST(check_process_record_with_incorrect_headers)
ck_assert_msg(cl != NULL, "failed to malloc client");
add_pollfd(&tdaemon, client_fd, POLLIN | POLLPRI);
post_was_called = false;
record = get_serialized_record(headers, post_body, &record_size);
ssize_t ret = write(server_fd, record, record_size);
ck_assert(ret == record_size);
processed = handle_client(&tdaemon, 0, cl);
ck_assert(processed == true);
ck_assert(post_was_called == false);
ck_assert_msg(is_client_list_empty(&(tdaemon.client_head)), "Failed to remove client with incorrect headers\n");
ck_assert_msg(tdaemon.nfds == 0, "Failed to remove poll fd for client with incorrect headers\n");
close(server_fd);
@@ -356,296 +335,13 @@ START_TEST(check_process_record_with_incorrect_headers)
}
END_TEST
START_TEST(check_rate_limit_enabled_functions)
{
setup();
bool record_check_passed = true;
bool byte_check_passed = true;
bool record_burst_enabled = true;
bool byte_burst_enabled = true;
tdaemon.record_burst_limit = 4;
tdaemon.byte_burst_limit = 2999;
tdaemon.rate_limit_enabled = true;
bool burst_limit_enabled(int64_t burst_limit)
{
return (burst_limit > -1) ? true : false;
}
/* Checks whether record burst rate limiting is enabled */
if (burst_limit_enabled(tdaemon.record_burst_limit)) {
record_check_passed = false;
} else {
/* If not enabled, does not need to be checked */
record_burst_enabled = false;
}
/* Checks whether byte burst rate limiting is enabled */
if (burst_limit_enabled(tdaemon.byte_burst_limit)) {
byte_check_passed = false;
} else {
/* If not enabled, does not need to be checked */
byte_burst_enabled = false;
}
if (!record_burst_enabled && !byte_burst_enabled) {
tdaemon.rate_limit_enabled = false;
}
ck_assert(record_check_passed == false);
ck_assert(byte_check_passed == false);
ck_assert(tdaemon.rate_limit_enabled == true);
}
END_TEST
START_TEST(check_rate_limit_records_that_pass)
{
setup();
int current_minute = 3;
tdaemon.record_burst_array[1] = 10;
tdaemon.record_burst_array[7] = 10;
tdaemon.record_burst_array[10] = 20;
tdaemon.record_burst_array[55] = 10;
tdaemon.record_burst_limit = 30;
tdaemon.record_window_length = 15;
bool checker;
checker = rate_limit_check(current_minute, tdaemon.record_burst_limit,
tdaemon.record_window_length, tdaemon.record_burst_array, 1);
ck_assert(checker == true);
}
END_TEST
START_TEST(check_rate_limit_records_that_do_not_pass)
{
setup();
int current_minute = 24;
tdaemon.record_burst_array[3] = 10;
tdaemon.record_burst_array[4] = 10;
tdaemon.record_burst_array[6] = 10;
tdaemon.record_burst_array[27] = 10;
tdaemon.record_burst_limit = 30;
tdaemon.record_window_length = 30;
bool checker;
checker = rate_limit_check(current_minute, tdaemon.record_burst_limit,
tdaemon.record_window_length, tdaemon.record_burst_array, 1);
ck_assert(checker == false);
}
END_TEST
START_TEST(check_rate_limit_bytes_that_pass)
{
setup();
size_t incValue = 20000;
int current_minute = 3;
tdaemon.byte_burst_array[1] = 32000;
tdaemon.byte_burst_array[7] = 10000;
tdaemon.byte_burst_array[10] = 45000;
tdaemon.byte_burst_array[55] = 11000;
tdaemon.byte_burst_limit = 64000;
tdaemon.byte_window_length = 15;
bool checker;
checker = rate_limit_check(current_minute, tdaemon.byte_burst_limit,
tdaemon.byte_window_length, tdaemon.byte_burst_array, incValue);
ck_assert(checker == true);
}
END_TEST
START_TEST(check_rate_limit_bytes_that_do_not_pass)
{
setup();
size_t incValue = 80000;
int current_minute = 15;
tdaemon.byte_burst_array[1] = 32000;
tdaemon.byte_burst_array[7] = 10000;
tdaemon.byte_burst_array[10] = 32300;
tdaemon.byte_burst_array[55] = 31000;
tdaemon.byte_burst_limit = 100000;
tdaemon.byte_window_length = 15;
bool checker;
checker = rate_limit_check(current_minute, tdaemon.byte_burst_limit,
tdaemon.byte_window_length, tdaemon.byte_burst_array, incValue);
ck_assert(checker == false);
}
END_TEST
START_TEST(check_update_record_array)
{
setup();
int current_minute = 7;
tdaemon.record_window_length = 15;
rate_limit_update(current_minute, tdaemon.record_window_length,
tdaemon.record_burst_array, TM_RECORD_COUNTER);
rate_limit_update(current_minute, tdaemon.record_window_length,
tdaemon.record_burst_array, TM_RECORD_COUNTER);
current_minute = 45;
rate_limit_update(current_minute, tdaemon.record_window_length,
tdaemon.record_burst_array, TM_RECORD_COUNTER);
current_minute = 47;
rate_limit_update(current_minute, tdaemon.record_window_length,
tdaemon.record_burst_array, TM_RECORD_COUNTER);
rate_limit_update(current_minute, tdaemon.record_window_length,
tdaemon.record_burst_array, TM_RECORD_COUNTER);
for (int i = 0; i < 60; i++) {
if (i != 45 && i != 47) {
ck_assert_msg(tdaemon.record_burst_array[i] == 0, "array at %d is %d\n",
i, tdaemon.record_burst_array[i]);
} else {
ck_assert_msg(tdaemon.record_burst_array[45] == 1, "array: %d is %d\n",
i, tdaemon.record_burst_array[i]);
ck_assert_msg(tdaemon.record_burst_array[47] == 2, "array: %d is %d\n",
i, tdaemon.record_burst_array[i]);
}
}
}
END_TEST
START_TEST(check_update_byte_array)
{
setup();
int current_minute = 7;
tdaemon.byte_window_length = 15;
size_t record_size = 32000;
rate_limit_update(current_minute, tdaemon.byte_window_length,
tdaemon.byte_burst_array, record_size);
rate_limit_update(current_minute, tdaemon.byte_window_length,
tdaemon.byte_burst_array, record_size);
current_minute = 45;
rate_limit_update(current_minute, tdaemon.byte_window_length,
tdaemon.byte_burst_array, record_size);
current_minute = 47;
rate_limit_update(current_minute, tdaemon.byte_window_length,
tdaemon.byte_burst_array, record_size);
rate_limit_update(current_minute, tdaemon.byte_window_length,
tdaemon.byte_burst_array, record_size);
rate_limit_update(current_minute, tdaemon.byte_window_length,
tdaemon.byte_burst_array, record_size);
for (int i = 0; i < 60; i++) {
if (i != 45 && i != 47) {
ck_assert_msg(tdaemon.byte_burst_array[i] == 0, "array at %d is %d\n",
i, tdaemon.record_burst_array[i]);
} else {
ck_assert_msg(tdaemon.byte_burst_array[45] == 32000, "bytearray: %d is %d\n",
i, tdaemon.record_burst_array[i]);
ck_assert_msg(tdaemon.byte_burst_array[47] == 96000, "bytearray: %d is %d\n",
i, tdaemon.record_burst_array[i]);
}
}
}
END_TEST
START_TEST(check_strategy_spool_option)
{
setup();
tdaemon.rate_limit_strategy = "spool";
bool do_spool = true;
char *ret = NULL;
bool record_sent = false;
// Get rate-limit strategy
do_spool = spool_strategy_selected(&tdaemon);
// Drop record
if (!record_sent && !do_spool) {
ret = "dropped";
}
// Spool Record
else if (!record_sent && do_spool) {
ret = "spooled";
} else {
ret = "sent";
}
ck_assert_str_eq(ret, "spooled");
}
END_TEST
START_TEST(check_strategy_drop_option)
{
setup();
tdaemon.rate_limit_strategy = "drop";
bool do_spool = true;
char *ret = NULL;
bool record_sent = false;
// Get rate-limit strategy
do_spool = spool_strategy_selected(&tdaemon);
// Drop record
if (!record_sent && !do_spool) {
ret = "dropped";
}
// Spool Record
else if (!record_sent && do_spool) {
ret = "spooled";
} else {
ret = "sent";
}
ck_assert_str_eq(ret, "dropped");
}
END_TEST
START_TEST(check_strategy_if_record_sent)
{
setup();
tdaemon.rate_limit_strategy = "drop";
bool do_spool = true;
char *ret = NULL;
bool record_sent = true;
// Get rate-limit strategy
do_spool = spool_strategy_selected(&tdaemon);
// Drop record
if (!record_sent && !do_spool) {
ret = "dropped";
}
// Spool Record
else if (!record_sent && do_spool) {
ret = "spooled";
} else {
ret = "sent";
}
ck_assert_str_eq(ret, "sent");
}
END_TEST
Suite *config_suite(void)
{
// A suite is comprised of test cases, defined below
Suite *s = suite_create("daemon");
Suite *s = suite_create("probd");
// Individual unit tests are added to "test cases"
TCase *t = tcase_create("daemon");
TCase *t = tcase_create("probd");
tcase_add_test(t, check_add_del_poll_fd);
tcase_add_test(t, check_daemon_is_initialized);
tcase_add_test(t, check_add_remove_client);
@@ -655,16 +351,6 @@ Suite *config_suite(void)
tcase_add_test(t, check_handle_client_with_correct_size);
tcase_add_test(t, check_process_record_with_correct_size_and_data);
tcase_add_test(t, check_process_record_with_incorrect_headers);
tcase_add_test(t, check_rate_limit_enabled_functions);
tcase_add_test(t, check_rate_limit_records_that_pass);
tcase_add_test(t, check_rate_limit_records_that_do_not_pass);
tcase_add_test(t, check_rate_limit_bytes_that_pass);
tcase_add_test(t, check_rate_limit_bytes_that_do_not_pass);
tcase_add_test(t, check_update_record_array);
tcase_add_test(t, check_update_byte_array);
tcase_add_test(t, check_strategy_spool_option);
tcase_add_test(t, check_strategy_drop_option);
tcase_add_test(t, check_strategy_if_record_sent);
suite_add_tcase(s, t);
-21
View File
@@ -40,27 +40,6 @@ void callback_func(struct oops_log_msg *msg)
pl = parse_payload(msg);
}
void setup(char *oopsfile)
{
char *buf = NULL;
int buflen = 0;
oops_parser_cleanup();
buf = readfile(oopsfile);
buflen = (int)getbuflen();
oops_parser_init(write_oops_to_file);
// Allocate space to write the oops file to
if (allocate_filespace() < 0) {
telem_log(LOG_ERR, "%s\n", strerror(errno));
exit(EXIT_FAILURE);
}
split_buf_by_line(buf, buflen);
}
void setup_payload(char *oopsfile)
{
char *buf = NULL;
+45 -7
View File
@@ -12,7 +12,8 @@ TESTS = $(check_PROGRAMS) $(dist_check_SCRIPTS)
check_PROGRAMS = \
%D%/check_config \
%D%/check_daemon \
%D%/check_probd \
%D%/check_postd \
%D%/check_probes \
%D%/check_ncb64 \
%D%/check_journal \
@@ -32,27 +33,63 @@ dist_check_SCRIPTS = \
@CHECK_LIBS@ \
$(top_builddir)/src/libtelem-shared.la
%C%_check_daemon_SOURCES = \
%D%/check_daemon.c \
%C%_check_probd_SOURCES = \
%D%/check_probd.c \
src/telemdaemon.c \
src/telemdaemon.h \
src/iorecord.h \
src/iorecord.c \
src/journal/journal.c \
src/journal/journal.h
%C%_check_daemon_CFLAGS = \
%C%_check_probd_CFLAGS = \
$(AM_CFLAGS) \
@CHECK_CFLAGS@ \
@CURL_CFLAGS@
%C%_check_daemon_LDADD = \
%C%_check_probd_LDADD = \
@CHECK_LIBS@ \
@CURL_LIBS@ \
$(top_builddir)/src/libtelem-shared.la
if LOG_SYSTEMD
if HAVE_SYSTEMD_JOURNAL
%C%_check_daemon_CFLAGS += \
%C%_check_probd_CFLAGS += \
$(SYSTEMD_JOURNAL_CFLAGS)
%C%_check_daemon_LDADD += \
%C%_check_probd_LDADD += \
$(SYSTEMD_JOURNAL_LIBS)
endif
endif
%C%_check_postd_SOURCES = \
%D%/check_postd.c \
src/spool.c \
src/iorecord.c \
src/retention.c \
src/telempostdaemon.c \
src/telempostdaemon.h \
src/journal/journal.c \
src/journal/journal.h
EXTRA_DIST += \
%D%/telempostd/correct_message \
%D%/telempostd/incorrect_headers \
%D%/telempostd/empty_message \
%D%/telempostd/incorrect_message
%C%_check_postd_CFLAGS = \
$(AM_CFLAGS) \
@CHECK_CFLAGS@ \
@CURL_CFLAGS@
%C%_check_postd_LDADD = \
@CHECK_LIBS@ \
@CURL_LIBS@ \
$(top_builddir)/src/libtelem-shared.la
if LOG_SYSTEMD
if HAVE_SYSTEMD_JOURNAL
%C%_check_postd_CFLAGS += \
$(SYSTEMD_JOURNAL_CFLAGS)
%C%_check_postd_LDADD += \
$(SYSTEMD_JOURNAL_LIBS)
endif
endif
@@ -99,6 +136,7 @@ EXTRA_DIST += \
@CHECK_CFLAGS@
%C%_check_probes_LDADD = \
@CHECK_LIBS@ \
$(top_builddir)/src/libtelemetry.la \
$(top_builddir)/src/libtelem-shared.la
if LOG_SYSTEMD
+16
View File
@@ -0,0 +1,16 @@
record_format_version: 1
classification: crash/kernel/bug
severity: 0
machine_id: 1234
creation_timestamp: 1418672344
arch: x86_64
host_type: macbookpro
build: 200
kernel_version: 3.15
payload_format_version: 1
system_name: clear-linux-os
board_name: Qemu|Intel
cpu_model: Intel(R) Core(TM) i7-5650U CPU @ 2.20GHz
bios_version: Qemu
event_id: 3a2d799826edc6266d72824d2aac6763
test message
View File
+10
View File
@@ -0,0 +1,10 @@
record_format_version: 1
nclassificatioooon: crash/kernel/bug
severity: 0
machine_id: 1234
creation_timestamp: 1531420323
arch: x86_64
host_type: macbookpro
build: 23550
kernel_version: 4.17.4-589.native
test message
+1
View File
@@ -0,0 +1 @@
test