Compare commits

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

Signed-off-by: avjarami <alex.v.jaramillo@intel.com>
2018-04-09 21:03:31 -07:00
79 changed files with 1687 additions and 3071 deletions
+2 -7
View File
@@ -33,8 +33,7 @@ build-aux/m4/ltversion.m4
*.trs
/cscope.*
/tags
telemprobd
telempostd
telemd
autoscan.log
configure.scan
tprobe
@@ -42,9 +41,9 @@ hprobe
bertprobe
crashprobe
journalprobe
pythonprobe
telem-record-gen
klogscanner
oopsprobe
pstoreclean
pstoreprobe
telem_journal
@@ -56,11 +55,7 @@ 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
+24 -81
View File
@@ -1,5 +1,3 @@
[![Build Status](https://travis-ci.org/clearlinux/telemetrics-client.svg?branch=master)](https://travis-ci.org/clearlinux/telemetrics-client)
Telemetrics-client
==================
@@ -8,15 +6,12 @@ solution for Linux-based operating systems. Specifically, the front end
component includes:
- telemetrics probes that collect specific types of data from the operating
system. For more info on probes go [here](./src/probes).
system.
- a library, libtelemetry, that telemetrics probes use to create telemetrics
records and send them to the daemon for further processing.
- telemprobd, a daemon that handles communication with probes and forwards records
to a second daemon.
- telempostd, this daemon sends telemetry records to a telemetrics server
- a daemon, telemd, that prepares the records to send to a telemetrics server
(not included in this source tree), or spools the records on disk in case
it's unable to successfully deliver them.
@@ -35,7 +30,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 telemprobd and telempostd by systemd.
socket/path activation of telemd by systemd.
Build and installation
@@ -65,38 +60,31 @@ and modify the /etc version.
Descriptions of config options are listed below in the Usage section.
Starting the client
Starting the daemon
---------------------
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}
telemctl start
systemctl start telemd.socket telemd.path
```
Note: the above invocation technically readies the service for both socket and
path activation, so you may not see an "active" status.
path activation, so you may not see the daemon start right away.
Method 2:
```{r, engine='bash', count_lines}
systemctl start telemprobd.service
systemctl start telempostd.service
systemctl start telemd.service
```
Method 3:
```{r, engine='bash', count_lines}
telemprobd &
telempostd &
telemd &
```
Configure the client to autostart at boot
Configure the daemon to autostart at boot
---------------------
Method 1 (recommended):
@@ -104,23 +92,22 @@ Method 1 (recommended):
Enable the socket-activated service and path unit:
```{r, engine='bash', count_lines}
systemctl enable telemprobd.socket telempostd.path
systemctl enable telemd.socket telemd.path
```
Method 2:
Enable services, which automatically enables the socket and path
Enable the service itself, which automatically enables the socket and path
units as well:
```{r, engine='bash', count_lines}
systemctl enable telemprobd.service
systemctl enable telempostd.service
systemctl enable telemd.service
```
Usage
---------------------
Once the client is running, the telemetrics probes will be ready to use.
Once the daemon is running, the telemetrics probes will be ready to use.
Available probes:
@@ -130,11 +117,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 client uses the following configuration options from the configuration file:
The daemon uses the following configuration options from the configuration file:
* server: This specifies the web server that the telempostd sends the telemetry records to
* server: This specifies the web server that the daemon sends the telemetry records to
* socket_path: This specifies the path of the unix domain socket that the
telemprobd listens on for connections from the probes
daemon listens on for connections from the probes
* spool_dir: This config option is related to spooling. If the daemon is not
able to send the telemetry records to the backend server due to reasons such
as the network availability, then it stores the records in a spool directory.
@@ -144,7 +131,7 @@ The client 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 telemprobd.service
systemctl restart telemd.service
```
* record_expiry: This is the time in minutes after which the records in the
@@ -191,45 +178,6 @@ The client uses the following configuration options from the configuration file:
telemetry records locally only.
Data reported
---------------------
The data reported by the telemetry client could be understood as two main sets:
metadata and a payload.
The metadata is used to report details of a machine's architecture. The
following are the metadata values currently collected (Record Format Version 4):
* record_format_version: version of the record, currently is 'Version 4'. This
value changes when new metadata is added.
* classification: this field is used to identify the type of record sent by a
specific client probe; classifications use the format DOMAIN/PROBE/REST, where
DOMAIN is the vendor of the probe, PROBE is the probe name, and REST is a
probe-defined field to classify what is contained in the payload.
* severity: this is an integer value between 1 and 4 where 1 is "low" and 4 is
"critical"
* machine_id: a machine identifier that is rotate every 3 days for privacy reasons.
* creation_timestamp: timestamp when the record was collected.
* arch: a string describing machine architecture i.e. 'x86_64'.
* host_type: a string with the combination of 'System Vendor', 'Product Name', and
'Product Version' read from dmi file system.
* build: OS build number.
* kernel_version: Kernel version.
* payload_format_version: version of the payload, currently is 'Version 1'.
* system_name: the value after 'ID=' from '/etc/os-release' (or distribution
provided folder)
* board_name: a string read from dmi file system that combines 'Board Name' and
'Board Vendor'.
* cpu_model: cpu model name extracted from '/proc/cpuinfo'.
* bios_version: BIOS version.
* event_id: an id to group multiple records if these were generated by a single
event occurrence.
The payload as mentioned above is reported by probes. The telemetry library adds
the metadata to the payload (done programatically when using library API) for
more information about probes go [here](./src/probes).
Machine id
---------------------
@@ -244,17 +192,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 client reads, at most, the first 32 characters from this file
The telemetry daemon reads, at most, the first 32 characters from this file
uses it for the machine id. You can put a string like 'my-machine-name' in this
file to easily identify your machine. Restart telemprobd for the machine id
file to easily identify your machine. Restart telemd for the machine id
changes to take effect by running:
```{r, engine='bash', count_lines}
# systemctl restart telemprobd.service
# systemctl restart telemd.service
```
You can switch back to the rotating machine id by deleting the override file
and restarting the client. You can do a quick test to check that your
and restarting the daemon. You can do a quick test to check that your
machine-id has changed by running "hprobe" and verifying that a record has
landed on your backend telemetrics server, with the specified machine id.
@@ -284,18 +232,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 telempostd to keep
* *Enabling record retention*: this step configures telemd to keep
copies of telemetry records locally. To enable record retention set the value of
```record_retention_enabled``` from ```false``` to ```true```. Optionally set
```record_server_delivery_enabled`` to ```false``` to keep records local only.
Remember to restart the client after configuration values are updated
Remember to restart the daemon after configuration values are updated
(```telemctl restart```).
* *Creating a record*: run ```hprobe``` command to create a record for the purposes
of this step by step guide. Once we have the record or records that you need to
capture locally you can display the data.
* *Displaying record metadata*: telempostd keeps metadata of any valid record,
* *Displaying record metadata*: telemd keeps metadata of any valid record,
to display this data a new option to telemctl was added ```telemctl journal```.
Assuming that the last record created was the record from previous step `hprobe` we
can use `tail -n 1` to print the last created record only, i.e.
@@ -323,8 +271,3 @@ $ 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,6 +24,7 @@ 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\"
+3 -3
View File
@@ -2,13 +2,13 @@
# Process this file with autoconf to produce a configure script.
AC_PREREQ([2.69])
AC_INIT([telemetrics-client], [2.1.0], [https://clearlinux.org/])
AC_INIT([telemetrics-client], [1.16.0], [https://clearlinux.org/])
AC_CONFIG_AUX_DIR([build-aux])
AM_INIT_AUTOMAKE([1.14 -Wall -Werror -Wno-extra-portability foreign subdir-objects])
AM_SILENT_RULES([yes])
LT_INIT([disable-static])
AC_CONFIG_MACRO_DIRS([build-aux/m4])
AC_CONFIG_SRCDIR([src/probe.c])
AC_CONFIG_SRCDIR([src/main.c])
AC_CONFIG_HEADERS([config.h])
# Checks for programs.
@@ -23,7 +23,7 @@ AC_PROG_MAKE_SET
# Checks for libraries.
# check >= 0.9.12 is required for TAP output
PKG_CHECK_MODULES([CHECK], [check >= 0.12])
PKG_CHECK_MODULES([CHECK], [check >= 0.9.12])
PKG_CHECK_MODULES([CURL], [libcurl])
AC_CHECK_LIB([elf], [elf_begin], [have_elflib=yes], [AC_MSG_ERROR([Unable to find libelf from elfutils])])
AC_CHECK_LIB([dw], [dwfl_begin], [have_dwlib=yes], [AC_MSG_ERROR([Unable to find libdw from elfutils])])
+3 -3
View File
@@ -1,9 +1,9 @@
## Notes on manual pages
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`
The manual pages are generated using `ronn(1)`. To recreate them,
run `make manpages` in the toplevel folder. If you want to edit
the documentation for upstream changes, make sure to edit the `*.md`
files and not the shipped nroff output files.
+1 -2
View File
@@ -1,7 +1,6 @@
MANPAGES = \
docs/man/telemctl.1 \
docs/man/telemprobd.1 \
docs/man/telempostd.1 \
docs/man/telemd.1 \
docs/man/telem-record-gen.1 \
docs/man/telemetry.3 \
docs/man/telemetrics.conf.5
+2 -5
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 \fBtelemprobd\fP(1). This
Generate and attempt to send a telemetry record to \fBtelemd\fP(1). This
program can be used to create a telemetry record from a shell script
or from another program using e.g. \fBsystem()\fP\&. The program connects
to a local \fBtelemprobd\fP(1) daemon to spool the record for delivery to
to a local \fBtelemd\fP(1) daemon to spool the record for delivery to
the actual collection service.
.sp
The payload can be provided as standard input, as a file with the
@@ -77,9 +77,6 @@ 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
+3 -6
View File
@@ -19,10 +19,10 @@ SYNOPSIS
DESCRIPTION
===========
Generate and attempt to send a telemetry record to ``telemprobd``\(1). This
Generate and attempt to send a telemetry record to ``telemd``\(1). This
program can be used to create a telemetry record from a shell script
or from another program using e.g. ``system()``. The program connects
to a local ``telemprobd``\(1) daemon to spool the record for delivery to
to a local ``telemd``\(1) daemon to spool the record for delivery to
the actual collection service.
The payload can be provided as standard input, as a file with the
@@ -62,10 +62,6 @@ 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
=============
@@ -79,3 +75,4 @@ SEE ALSO
* ``telemd``\(1)
* https://github.com/clearlinux/telemetrics-client
* https://clearlinux.org/documentation/
+2 -4
View File
@@ -38,7 +38,8 @@ 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 \fBtelemprobd\fP(1) and \fBtelempostd\fP(1), or to opt\-in or opt\-out of telemetry delivery of records to a central telemetry service.
restart, or stop \fBtelemd\fP(1), or to opt\-in or opt\-out of telemetry delivery
of records to a central telemetry service.
.SH OPTIONS
.INDENT 0.0
.INDENT 3.5
@@ -54,9 +55,6 @@ 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
+5 -6
View File
@@ -22,7 +22,8 @@ DESCRIPTION
===========
Control actions for telemetry services. The command can be used to start,
restart, or stop ``telemprobd``\(1) and ``telempostd``\(1), or to opt-in or opt-out of telemetry delivery of records to a central telemetry service.
restart, or stop ``telemd``\(1), or to opt-in or opt-out of telemetry delivery
of records to a central telemetry service.
OPTIONS
@@ -31,17 +32,14 @@ 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
=============
@@ -55,3 +53,4 @@ SEE ALSO
* ``telemd``\(1)
* https://github.com/clearlinux/telemetrics-client
* https://clearlinux.org/documentation/
+7 -7
View File
@@ -1,8 +1,8 @@
.\" Man page generated from reStructuredText.
.
.TH TELEMPROBD 1 "" "" ""
.TH TELEMD 1 "" "" ""
.SH NAME
telemprobd \- Telemetry probes service
telemd \- Telemetry client service
.
.nr rst2man-indent-level 0
.
@@ -32,7 +32,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
..
.SH SYNOPSIS
.sp
\fBtelemprobd\fP <flags>
\fBtelemd\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 \fBtelemprobd\fP program handles communication between telemetry client and telemetry
probes.
The \fBtelemd\fP program delivers locally generated telemetry records to a remote
telemetry service. Telemetry data can be in any format, and is relayed as\-is.
.SH OPTIONS
.INDENT 0.0
.INDENT 3.5
@@ -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, \fBtelemprobd\fP uses the
If no custom configuration file is found, \fBtelemd\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 \fBtelemprobd\fP reads. See \fBtelemetrics.conf\fP(5).
Custom configuration file that \fBtelemd\fP reads. See \fBtelemetrics.conf\fP(5).
.UNINDENT
.UNINDENT
.IP \(bu 2
@@ -1,9 +1,9 @@
==========
telemprobd
==========
======
telemd
======
------------------------
Telemetry probes service
Telemetry client service
------------------------
:Copyright: \(C) 2017 Intel Corporation, CC-BY-SA-3.0
@@ -13,7 +13,7 @@ Telemetry probes service
SYNOPSIS
========
``telemprobd`` \<flags\>
``telemd`` \<flags\>
``/etc/telemetrics/telemetrics.conf``
@@ -25,8 +25,8 @@ SYNOPSIS
DESCRIPTION
===========
The ``telemprobd`` program handles communication between telemetry client and telemetry
probes.
The ``telemd`` program delivers locally generated telemetry records to a remote
telemetry service. Telemetry data can be in any format, and is relayed as-is.
OPTIONS
@@ -47,12 +47,12 @@ FILES
* ``/usr/share/defaults/telemetrics/telemetrics.conf``
If no custom configuration file is found, ``telemprobd`` uses the
If no custom configuration file is found, ``telemd`` uses the
settings in this file.
* ``/etc/telemetrics/telemetrics.conf``
Custom configuration file that ``telemprobd`` reads. See ``telemetrics.conf``\(5).
Custom configuration file that ``telemd`` reads. See ``telemetrics.conf``\(5).
* ``/etc/telemetrics/opt-in-static-machine-id``
+5 -6
View File
@@ -37,7 +37,8 @@ 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 \fBtelemprobd\fP(1) and \fBtelempostd\fP(1) telemetry service daemons. The daemon reads this file at startup if it exists.
This file contains configuration parameters for the \fBtelemd\fP(1) telemetry
service daemon. The daemon reads this file at startup if it exists.
.SH SYNTAX
.sp
The configuration file contains \fBkey=value\fP pairs, formatted as plain
@@ -53,7 +54,7 @@ Server URL including protocol designator.
.IP \(bu 2
\fBsocket_path=<path>\fP
.sp
Path to the socket that \fItelemprobd\fP will listen on.
Path to the socket that \fItelemd\fP will listen on.
.IP \(bu 2
\fBcainfo=<path>\fP
.sp
@@ -81,7 +82,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..300. Values
Time in seconds for processing spool. Valid range: 120..3600. Values
outside this range are clamped.
.IP \(bu 2
\fBrate_limit_enabled=<true|false>\fP
@@ -113,9 +114,7 @@ delivery over network. Valid stategies: \fBspool\fP, \fBdrop\fP\&.
.SH SEE ALSO
.INDENT 0.0
.IP \(bu 2
\fBtelemprobd\fP(1)
.IP \(bu 2
\fBtelempostd\fP(1)
\fBtelemd\fP(1)
.IP \(bu 2
\fI\%https://github.com/clearlinux/telemetrics\-client\fP
.IP \(bu 2
+6 -5
View File
@@ -21,7 +21,8 @@ SYNOPSIS
DESCRIPTION
===========
This file contains configuration parameters for the ``telemprobd``\(1) and ``telempostd``\(1) telemetry service daemons. The daemon reads this file at startup if it exists.
This file contains configuration parameters for the ``telemd``\(1) telemetry
service daemon. The daemon reads this file at startup if it exists.
SYNTAX
@@ -42,7 +43,7 @@ OPTIONS
- ``socket_path=<path>``
Path to the socket that `telemprobd` will listen on.
Path to the socket that `telemd` will listen on.
- ``cainfo=<path>``
@@ -70,7 +71,7 @@ OPTIONS
- ``spool_process_time=<seconds>``
Time in seconds for processing spool. Valid range: 120..300. Values
Time in seconds for processing spool. Valid range: 120..3600. Values
outside this range are clamped.
- ``rate_limit_enabled=<true|false>``
@@ -103,7 +104,7 @@ OPTIONS
SEE ALSO
========
* ``telemprobd``\(1)
* ``telempostd``\(1)
* ``telemd``\(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
\fBint tm_set_config_file(const char *c_file)\fP
\fBvoid tm_set_config_file(char *c_file)\fP
.SH DESCRIPTION
.sp
The functions in the telemetry library facilitate the delivery of
telemetry data to the \fBtelemprobd\fP(1) service.
telemetry data to the \fBtelemd\fP(1) service.
.sp
The function \fBtm_create_record()\fP initializes a telemetry record and
sets the severity and classification of that record, as well as the
@@ -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 function \fBtm_free_record()\fP does not return
any value.
if an error occurred. The functions \fBtm_free_record()\fP and \fBtm_set_config_file()\fP
do not return any values.
.SH SEE ALSO
.INDENT 0.0
.IP \(bu 2
\fBtelemprobd\fP(1)
\fBtelemd\fP(1)
.IP \(bu 2
\fI\%https://github.com/clearlinux/telemetrics\-client\fP
.IP \(bu 2
+8 -7
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)``
``int tm_set_config_file(const char *c_file)``
``void tm_set_config_file(char *c_file)``
DESCRIPTION
===========
The functions in the telemetry library facilitate the delivery of
telemetry data to the ``telemprobd``\(1) service.
telemetry data to the ``telemd``\(1) service.
The function ``tm_create_record()`` initializes a telemetry record and
sets the severity and classification of that record, as well as the
@@ -54,13 +54,14 @@ RETURN VALUES
=============
All these functions return ``0`` on success, or a non-zero return value
if an error occurred. The function ``tm_free_record()`` does not return
any value.
if an error occurred. The functions ``tm_free_record()`` and ``tm_set_config_file()``
do not return any values.
SEE ALSO
========
* ``telemprobd``\(1)
* ``telemd``\(1)
* https://github.com/clearlinux/telemetrics-client
* https://clearlinux.org/documentation/
-97
View File
@@ -1,97 +0,0 @@
.\" 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
@@ -1,71 +0,0 @@
==========
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/
+9 -12
View File
@@ -2,19 +2,17 @@
declare -a SPECIAL_UNITS=(
hprobe.timer
telemprobd.socket
telempostd.path
klogscanner.service
journal-probe-tail.service
python-probe.path
telemd.socket
telemd.path
)
declare -a SERVICES=(
hprobe.service
pstore-probe.service
telemprobd.service
telempostd.service
journal-probe.service
oops-probe.service
pstore-probe.service
klogscanner.service
telemd.service
)
SCRIPT="$0"
@@ -71,8 +69,7 @@ telem_start() {
telem_is_active() {
# check only activation units
echo "telemprobd :" $(systemctl is-active telemprobd.socket)
echo "telempostd :" $(systemctl is-active telempostd.path)
systemctl is-active telemd.socket
}
telem_opt_out() {
@@ -106,10 +103,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 telemprobd and telempostd are active"
printf "$format" "is-active" "Checks if telemd is active"
printf "$format" "opt-in" "Opts in to telemetry, and starts telemetry services"
printf "$format" "opt-out" "Opts out of telemetry, and stops telemetry services"
printf "$format" "journal" "Prints telemtry journal contents. Use -h argument with"
printf "$format" "journal" "Prints telemd journal contents. Use -h argument with"
printf "$format" "" "command for more options"
printf "\n"
exit 2
+8 -5
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_LENGTH 8192
#define MAX_PAYLOAD_SIZE 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
/* Exit daemon after idle for this duration in seconds */
#define TM_DAEMON_EXIT_TIME (5 * 60)
/* Max Payload size is 8 kibabytes (8192) */
#define MAX_PAYLOAD_LENGTH 8192
/* SPOOL DEFINES */
/* Spooling should run at least every TM_SPOOL_RUN_MAX */
#define TM_SPOOL_RUN_MAX TM_DAEMON_EXIT_TIME
/* Spooling should run every TM_SPOOL_RUN_MAX atleast */
#define TM_SPOOL_RUN_MAX (60 /*min*/ * 60 /*sec*/)
/* Spooling should not run more often than TM_SPOOL_RUN_MIN */
#define TM_SPOOL_RUN_MIN (2 /*min*/ * 60 /*sec*/)
@@ -102,6 +102,9 @@ 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.
*/
+9 -17
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 };
static int validate_config_file(const char *f)
int validate_config_file(char *f)
{
struct stat sbuf;
@@ -60,34 +60,26 @@ static int validate_config_file(const 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;
}
int set_config_file(const char *filename)
void set_config_file(char *filename)
{
int ret;
if ((ret = validate_config_file(filename)) == 0) {
char *filename1 = strdup(filename);
if (filename1 == NULL) {
ret = -errno;
} else {
config_file = filename1;
cmd_line_cfg = true;
}
/* 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;
}
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 */
int set_config_file(const char *filename);
void set_config_file(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=telemprobd.socket
After=telemprobd.socket
Requires=telemd.socket
After=telemd.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=180
spool_process_time=900
#rate limiting record burst limit
record_burst_limit=100
+3 -3
View File
@@ -1,11 +1,11 @@
[Unit]
Description=Telemetrics Heartbeat Probe
Requires=telemprobd.socket
After=telemprobd.socket
Requires=telemd.socket
After=telemd.socket
ConditionPathExists=!/etc/telemetrics/opt-out
[Service]
ExecStart=@bindir@/hprobe -Hlu
ExecStart=@bindir@/hprobe -H
User=telemetry
[Install]
-12
View File
@@ -1,12 +0,0 @@
[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=telemprobd.socket
After=telemprobd.socket
Requires=telemd.socket
After=telemd.socket
ConditionPathExists=!/etc/telemetrics/opt-out
[Service]
+2 -2
View File
@@ -1,7 +1,7 @@
[Unit]
Description=Telemetrics Kernel Log Scanner
Requires=telemprobd.socket
After=telemprobd.socket
Requires=telemd.socket
After=telemd.socket oops-probe.service
[Service]
ExecStart=@bindir@/klogscanner
+20 -38
View File
@@ -16,18 +16,15 @@ EXTRA_DIST += \
%D%/hprobe.timer \
%D%/bert-probe.service.in \
%D%/journal-probe.service.in \
%D%/journal-probe-tail.service.in \
%D%/python-probe.service.in \
%D%/python-probe.path.in \
%D%/oops-probe.service.in \
%D%/pstore-probe.service.in \
%D%/klogscanner.service.in \
%D%/pstore-clean.service.in \
%D%/libtelemetry.pc.in \
%D%/telempostd.service.in \
%D%/telempostd.path.in \
%D%/telemprobd.service.in \
%D%/telemprobd.socket.in \
%D%/telemprobd-update-trigger.service.in \
%D%/telemd.path.in \
%D%/telemd.service.in \
%D%/telemd.socket.in \
%D%/telemd-update-trigger.service.in \
%D%/telemetrics-dirs.conf.in \
%D%/telemetrics.conf.in
@@ -55,17 +52,14 @@ systemdunit_DATA = \
%D%/hprobe.timer \
%D%/bert-probe.service \
%D%/journal-probe.service \
%D%/journal-probe-tail.service \
%D%/python-probe.service \
%D%/python-probe.path \
%D%/oops-probe.service \
%D%/pstore-probe.service \
%D%/klogscanner.service \
%D%/pstore-clean.service \
%D%/telemprobd.service \
%D%/telemprobd.socket \
%D%/telemprobd-update-trigger.service \
%D%/telempostd.service \
%D%/telempostd.path
%D%/telemd.service \
%D%/telemd.socket \
%D%/telemd-update-trigger.service \
%D%/telemd.path
%D%/hprobe.service: %D%/hprobe.service.in
$(pathfix) < $< > $@
@@ -76,7 +70,7 @@ systemdunit_DATA = \
%D%/journal-probe.service: %D%/journal-probe.service.in
$(pathfix) < $< > $@
%D%/journal-probe-tail.service: %D%/journal-probe-tail.service.in
%D%/oops-probe.service: %D%/oops-probe.service.in
$(pathfix) < $< > $@
%D%/pstore-probe.service: %D%/pstore-probe.service.in
@@ -88,25 +82,16 @@ systemdunit_DATA = \
%D%/pstore-clean.service: %D%/pstore-clean.service.in
$(pathfix) < $< > $@
%D%/telempostd.path: %D%/telempostd.path.in
%D%/telemd.service: %D%/telemd.service.in
$(pathfix) < $< > $@
%D%/telempostd.service: %D%/telempostd.service.in
%D%/telemd.socket: %D%/telemd.socket.in
$(pathfix) < $< > $@
%D%/python-probe.service: %D%/python-probe.service.in
%D%/telemd.path: %D%/telemd.path.in
$(pathfix) < $< > $@
%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
%D%/telemd-update-trigger.service: %D%/telemd-update-trigger.service.in
$(pathfix) < $< > $@
sysctldir = @SYSTEMD_SYSCTLDIR@
@@ -119,19 +104,16 @@ systemconfdir = @SYSTEMD_SYSTEMCONFDIR@
systemconf_DATA = %D%/40-core-ulimit.conf
clean-local:
-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 \
-rm -f %D%/telemd.service \
%D%/telemd.socket \
%D%/telemd.path \
%D%/telemd-update-trigger.service \
%D%/telemetrics.conf \
%D%/telemetrics-dirs.conf \
%D%/libtelemetry.pc \
%D%/40-crash-probe.conf \
%D%/journal-probe.service \
%D%/journal-probe-tail.service \
%D%/oops-probe.service \
%D%/pstore-probe.service \
%D%/klogscanner.service \
%D%/pstore-clean.service \
+11
View File
@@ -0,0 +1,11 @@
[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=telemprobd.socket
After=telemprobd.socket
Requires=telemd.socket
After=telemd.socket
[Service]
ExecStart=@bindir@/pstoreprobe
-9
View File
@@ -1,9 +0,0 @@
[Unit]
Description=Python Probe Daemon Staging
ConditionPathExists=!/etc/telemetrics/opt-out
[Path]
DirectoryNotEmpty=@localstatedir@/lib/telemetry/python
[Install]
WantedBy=multi-user.target
-13
View File
@@ -1,13 +0,0 @@
[Unit]
Description=Telemetrics Python Probe
Requires=telemprobd.socket
After=telemprobd.socket
ConditionPathExists=!/etc/telemetrics/opt-out
[Service]
User=telemetry
ExecStart=@bindir@/pythonprobe
[Install]
WantedBy=multi-user.target
@@ -4,4 +4,4 @@ BindsTo=update-triggers.target
[Service]
Type=oneshot
ExecStart=@systemctldir@/systemctl try-restart telemprobd.service
ExecStart=@systemctldir@/systemctl try-restart telemd.service
@@ -1,5 +1,5 @@
[Unit]
Description=Telemetry Post Daemon staging
Description=Telemetry Daemon Spool
ConditionPathExists=!/etc/telemetrics/opt-out
[Path]
@@ -3,10 +3,10 @@ Description=Telemetrics Daemon
ConditionPathExists=!/etc/telemetrics/opt-out
[Service]
ExecStart=@bindir@/telemprobd
ExecStart=@bindir@/telemd
WorkingDirectory=@localstatedir@/spool/telemetry
User=telemetry
[Install]
Also=telemprobd.socket
Also=telemd.socket telemd.path
WantedBy=multi-user.target
+1 -1
View File
@@ -1,7 +1,7 @@
d @localstatedir@/lib/telemetry 0755 telemetry telemetry -
d @localstatedir@/lib/telemetry/python 01777 telemetry telemetry -
d @localstatedir@/spool/telemetry 0750 telemetry telemetry -
d @localstatedir@/log/telemetry 0750 telemetry telemetry -
d @localstatedir@/log/telemetry/records 0750 telemetry telemetry -
d @localstatedir@/cache/telemetry 0750 telemetry telemetry -
d @localstatedir@/cache/telemetry/oops 0750 telemetry telemetry -
d @localstatedir@/cache/telemetry/pstore 0750 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..300. Values outside this range are clamped.
spool_process_time=120
# Valid range: 120..3600. Values outside this range are clamped.
spool_process_time=900
# rate limit enabled - if this is set to false then all rate-limiting disabled.
# It is possible to disable each rate-limit individually below.
-12
View File
@@ -1,12 +0,0 @@
[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
-162
View File
@@ -1,162 +0,0 @@
/*
* This program is part of the Clear Linux Project
*
* Copyright 2018 Intel Corporation
*
* This program is free software; you can redistribute it and/or modify it under
* the terms and conditions of the GNU Lesser General Public License, as
* published by the Free Software Foundation; either version 2.1 of the License,
* or (at your option) any later version.
*
* This program is distributed in the hope it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*/
#define _GNU_SOURCE
#include <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
@@ -1,38 +0,0 @@
/*
* This program is part of the Clear Linux Project
*
* Copyright 2018 Intel Corporation
*
* This program is free software; you can redistribute it and/or modify it under
* the terms and conditions of the GNU Lesser General Public License, as
* published by the Free Software Foundation; either version 2.1 of the License,
* or (at your option) any later version.
*
* This program is distributed in the hope it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*/
#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);
+3 -5
View File
@@ -344,11 +344,9 @@ TelemJournal *open_journal(const char *journal_file)
struct TelemJournal *telem_journal;
// Use default location if journal_file parameter is NULL
if (journal_file == NULL) {
fptr = fopen(JOURNAL_PATH, "a+");
} else {
fptr = fopen(journal_file, "a+");
}
fptr = (journal_file == NULL) ? fopen(JOURNAL_PATH, "a+") :
fopen(journal_file, "a+");
if (fptr == NULL) {
#ifdef DEBUG
perror("Error while opening journal file: ");
+18 -59
View File
@@ -1,80 +1,39 @@
bin_PROGRAMS = \
%D%/telemprobd \
%D%/telempostd
bin_PROGRAMS = %D%/telemd
%C%_telemprobd_SOURCES = \
%D%/probe.c \
%C%_telemd_SOURCES = \
%D%/main.c \
%D%/telemdaemon.c \
%D%/telemdaemon.h \
%D%/iorecord.c \
%D%/journal/journal.c \
%D%/journal/journal.h
%C%_telemprobd_LDADD = $(CURL_LIBS) \
%D%/libtelem-shared.la \
%D%/libtelemetry.la
%C%_telemprobd_CFLAGS = \
$(AM_CFLAGS)
%C%_telemprobd_LDFLAGS = \
$(AM_LDFLAGS) \
-pie
if HAVE_SYSTEMD_DAEMON
%C%_telemprobd_CFLAGS += \
$(SYSTEMD_DAEMON_CFLAGS)
%C%_telemprobd_LDADD += \
$(SYSTEMD_DAEMON_LIBS)
endif
if LOG_SYSTEMD
if HAVE_SYSTEMD_JOURNAL
%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%/spool.c \
%D%/spool.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
%D%/retention.h
%C%_telempostd_LDADD = $(CURL_LIBS) \
%D%/libtelem-shared.la \
%D%/libtelemetry.la
%C%_telemd_LDADD = $(CURL_LIBS) \
%D%/libtelem-shared.la
%C%_telempostd_CFLAGS = \
%C%_telemd_CFLAGS = \
$(AM_CFLAGS)
%C%_telempostd_LDFLAGS = \
%C%_telemd_LDFLAGS = \
$(AM_LDFLAGS) \
-pie
if HAVE_SYSTEMD_DAEMON
%C%_telempostd_CFLAGS += \
$(SYSTEMD_DAEMON_CFLAGS)
%C%_telempostd_LDADD += \
$(SYSTEMD_DAEMON_LIBS)
%C%_telemd_CFLAGS += \
$(SYSTEMD_DAEMON_CFLAGS)
%C%_telemd_LDADD += \
$(SYSTEMD_DAEMON_LIBS)
endif
if LOG_SYSTEMD
if HAVE_SYSTEMD_JOURNAL
%C%_telempostd_CFLAGS += \
$(SYSTEMD_JOURNAL_CFLAGS)
%C%_telempostd_LDADD += \
$(SYSTEMD_JOURNAL_LIBS)
%C%_telemd_CFLAGS += \
$(SYSTEMD_JOURNAL_CFLAGS)
%C%_telemd_LDADD += \
$(SYSTEMD_JOURNAL_LIBS)
endif
endif
+58 -12
View File
@@ -31,7 +31,6 @@
#include <signal.h>
#include <malloc.h>
#include "telemetry.h"
#include "config.h"
#include "common.h"
#ifdef HAVE_SYSTEMD_SD_DAEMON_H
@@ -50,12 +49,15 @@
#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)
{
@@ -75,7 +77,9 @@ 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;
@@ -92,12 +96,18 @@ int main(int argc, char **argv)
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"
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"
" path not valid\n");
exit(EXIT_FAILURE);
exit(EXIT_FAILURE);
}
set_config_file(config_file);
break;
case 'h':
print_usage(argv[0]);
@@ -113,6 +123,11 @@ 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) {
@@ -212,9 +227,19 @@ int main(int argc, char **argv)
telem_log(LOG_INFO, "Listening on socket...\n");
bool daemon_recycling_enabled = daemon_recycling_enabled_config();
/* 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();
time_t last_record_received = time(NULL);
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();
ret = update_machine_id();
if (ret == -1) {
@@ -226,9 +251,18 @@ 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");
@@ -258,6 +292,12 @@ 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();
}
}
}
@@ -309,20 +349,25 @@ 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);
/* time to recycle the daemon has elapsed*/
if (daemon_recycling_enabled &&
difftime(now, last_record_received) >= TM_DAEMON_EXIT_TIME) {
/* 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) {
/* 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) {
@@ -333,13 +378,14 @@ 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");
}
-91
View File
@@ -1,91 +0,0 @@
/*
* This program is part of the Clear Linux Project
*
* Copyright 2018 Intel Corporation
*
* This program is free software; you can redistribute it and/or modify it under
* the terms and conditions of the GNU Lesser General Public License, as
* published by the Free Software Foundation; either version 2.1 of the License,
* or (at your option) any later version.
*
* This program is distributed in the hope it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*/
#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;
}
-55
View File
@@ -1,55 +0,0 @@
# Probes
The default probes provided along the telemetry client code are:
* bertprobe: this probe reports on the Boot Error Region Table if such
entry is found in ```/sys/firmware/acpi/tables/BERT```.
* crashprobe: This probe processes core dump files. It can be registered as
the kernel core file handler in /proc/sys/kernel/core_pattern.
* hprobe: a simple probe that sends a keep alive message. This probe can also
be useful for testing
* journalprobe: a probe that monitors the systemd journal for log messages
from failed services.
* klogscanner: a probe to collect 'oops messages' when the kernel detects a
problem.
* 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.
For example to generate an hprobe like message we can execute the following
command:
```
$ telem-record-gen --class org.example/hello/world --payload hello
```
for more options use the ```--help``` switch i.e.:
```
$ telem-record-gen --help
Usage:
telem-record-gen [OPTIONS] - create and send a custom telemetry record
Help Options:
-h, --help Show help options
Application Options:
-f, --config-file Path to configuration file (not implemented yet)
-V, --version Print the program version
-s, --severity Severity level (1-4) - (default 1)
-c, --class Classification level_1/level_2/level_3
-p, --payload Record body (max size = 8k)
-P, --payload-file File to read payload from
-R, --record-version Version number for format of payload (default 1)
-e, --event-id Event id to use in the record
```
+45 -20
View File
@@ -25,7 +25,6 @@
#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";
@@ -40,16 +39,35 @@ 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;
int ret;
char *payload = NULL;
// 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' },
@@ -57,14 +75,22 @@ int main(int argc, char **argv)
{ NULL, 0, NULL, 0 }
};
while ((c = getopt_long(argc, argv, "f:hV", opts, &opt_index)) != -1) {
while ((c = getopt_long(argc, argv, "f:hHV", 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);
if (asprintf(&cfile, "%s", (char *)optarg) < 0) {
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]);
@@ -77,37 +103,36 @@ int main(int argc, char **argv)
}
}
payload = calloc(sizeof(char), MAX_PAYLOAD_LENGTH);
if (!payload) {
if (!(ret = allocate_payload_buffer(&payload))) {
printf("Unable to allocate more memory.\n");
return -ENOMEM;
goto fail;
}
if (!(ret = nc_b64enc_filename(bert_record_file, payload, MAX_PAYLOAD_LENGTH))) {
if (!(ret = nc_b64enc_filename(bert_record_file, payload, MAX_PAYLOAD_SIZE))) {
printf("Failed to read payload from: %s\n", bert_record_file);
ret = EXIT_FAILURE;
goto done;
goto fail;
}
if ((ret = tm_create_record(&tm_handle, severity, classification,
payload_version)) < 0) {
printf("Failed to create record: %s\n", strerror(-ret));
goto done;
goto fail;
}
if ((ret = tm_set_payload(tm_handle, payload)) < 0) {
printf("Failed to set record payload: %s\n", strerror(-ret));
goto done;
goto fail;
}
free(payload);
if ((ret = tm_send_record(tm_handle)) < 0) {
printf("Failed to send record to daemon: %s\n", strerror(-ret));
goto done;
goto fail;
}
ret = EXIT_SUCCESS;
done:
free(payload);
fail:
free(cfile);
tm_free_record(tm_handle);
tm_handle = NULL;
+21 -10
View File
@@ -69,6 +69,7 @@ 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 =
{
@@ -462,6 +463,7 @@ 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;
@@ -488,7 +490,7 @@ static void print_help(void)
printf(" -h, --help Show help options\n");
printf("\n");
printf("Application Options:\n");
printf(" -f, --config_file Specify a configuration file other than default\n");
printf(" -f, --config-file Path to configuration file (not implemented yet)\n");
printf(" -c, --core-file Path to core file to process\n");
printf(" -p, --process-name Name of process for crash report (required)\n");
printf(" -E, --process-path Absolute path of crashed process, with ! or / delimiters\n");
@@ -530,11 +532,7 @@ int main(int argc, char **argv)
printf(PACKAGE_VERSION "\n");
goto success;
case 'f':
if (tm_set_config_file(optarg) != 0) {
telem_log(LOG_ERR, "Configuration file"
" path not valid\n");
exit(EXIT_FAILURE);
}
config_file = strdup(optarg);
break;
case 'c':
core_file = strdup(optarg);
@@ -608,6 +606,8 @@ 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,6 +619,8 @@ 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;
}
@@ -639,12 +641,16 @@ 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. Also retry if errors occur in the first run.
* seconds and try again.
*/
if ((process_corefile(&backtrace) < 0) || (strstr(backtrace->str, "??? - ["))) {
if (strstr(backtrace->str, "??? - [")) {
sleep(10);
if (prepare_corefile(&e_core, core_fd) < 0) {
@@ -671,6 +677,11 @@ 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);
@@ -700,8 +711,8 @@ fail:
}
// Remove the core file by default, except when the --core-file option
// is specified.
if (!core_file) {
// is specified, or when keep_core is overridden to true.
if (!core_file && !keep_core) {
unlink(temp_core);
}
+33 -131
View File
@@ -1,7 +1,7 @@
/*
* This program is part of the Clear Linux Project
*
* Copyright 2015-2019 Intel Corporation
* 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
@@ -20,156 +20,63 @@
#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"
#define PAYLOAD_LOCALE 0x01
#define PAYLOAD_UPTIME 0x02
#define PAYLOAD_BUNDLES 0x04
static void print_usage(char *prog)
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 *payload;
char *payload2;
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:hHVlub", opts, &opt_index)) != -1) {
while ((c = getopt_long(argc, argv, "f:hHV", 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);
if (asprintf(&cfile, "%s", (char *)optarg) < 0) {
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);
@@ -181,39 +88,34 @@ int main(int argc, char **argv)
}
}
setlocale(LC_ALL, "");
payload = create_payload(payload_options);
if (payload == NULL) {
if (asprintf(&payload2, "hello\n") < 0) {
exit(EXIT_FAILURE);
}
if ((ret = tm_create_record(&tm_handle, severity, classification,
payload_version)) < 0) {
telem_log(LOG_ERR, "Failed to create record: %s\n",
strerror(-ret));
ret = EXIT_FAILURE;
goto done;
printf("Failed to create record: %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;
if ((ret = tm_set_payload(tm_handle, payload2)) < 0) {
printf("Failed to set record payload: %s\n", strerror(-ret));
ret = 1;
goto fail;
}
free(payload);
free(payload2);
if ((ret = tm_send_record(tm_handle)) < 0) {
telem_log(LOG_ERR, "Failed to send record to daemon: %s\n",
strerror(-ret));
ret = EXIT_FAILURE;
goto done;
printf("Failed to send record to daemon: %s\n", strerror(-ret));
ret = 1;
goto fail;
} else {
printf("Successfully sent record to daemon.\n");
ret = EXIT_SUCCESS;
ret = 0;
}
done:
fail:
tm_free_record(tm_handle);
tm_handle = NULL;
+16 -23
View File
@@ -265,7 +265,7 @@ static bool add_filters(sd_journal *journal)
return true;
}
static bool process_journal(bool process_existing)
static bool process_journal(void)
{
int r;
@@ -279,14 +279,8 @@ static bool process_journal(bool process_existing)
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;
}
if (!process_existing_entries(journal)) {
return false;
}
struct pollfd pfd;
@@ -318,11 +312,12 @@ static bool process_journal(bool process_existing)
}
}
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 }
};
@@ -335,19 +330,22 @@ static void print_help(void)
printf(" -h, --help Show help options\n");
printf("\n");
printf("Application Options:\n");
printf(" -f, --config_file Specify a configuration file other than default\n");
printf(" -t, --tail Don't process entries already in the journal\n");
printf(" -f, --config-file Path to configuration file (not implemented yet)\n");
printf(" -V, --version Print the program version\n");
printf("\n");
}
static void free_strings(void)
{
free(config_file);
}
int main(int argc, char **argv)
{
int ret = EXIT_FAILURE;
int opt;
bool process_existing = true;
while ((opt = getopt_long(argc, argv, "hf:Vt", prog_opts, NULL)) != -1) {
while ((opt = getopt_long(argc, argv, "hf:V", prog_opts, NULL)) != -1) {
switch (opt) {
case 'h':
print_help();
@@ -356,25 +354,20 @@ int main(int argc, char **argv)
printf(PACKAGE_VERSION "\n");
goto success;
case 'f':
if (tm_set_config_file(optarg) != 0) {
telem_log(LOG_ERR, "Configuration file"
" path not valid\n");
exit(EXIT_FAILURE);
}
break;
case 't':
process_existing = false;
config_file = strdup(optarg);
break;
}
}
if (!process_journal(process_existing)) {
if (!process_journal()) {
goto fail;
}
success:
ret = EXIT_SUCCESS;
fail:
free_strings();
if (journal) {
sd_journal_close(journal);
}
+34 -43
View File
@@ -1,24 +1,9 @@
/*
* 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>
@@ -37,48 +22,54 @@ int main(void)
int log_size = 0;
char *bufp = NULL;
size_t buflen = 0;
int bytes = 0;
oops_parser_init(klog_process_oops_msgs);
oops_parser_init(write_oops_to_file);
// 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_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));
// Allocate space to write the oops file to
if (allocate_filespace() < 0) {
telem_log(LOG_ERR, "%s\n", strerror(errno));
return 1;
}
buflen = (size_t)log_size;
if (buflen > MAX_BUF) {
buflen = MAX_BUF;
// Signal Handling to close and unlink empty files
if (signal (SIGINT, signal_handler) == SIG_ERR) {
telem_log(LOG_ERR, "Error handling interrupt signal\n");
}
if (signal (SIGTERM, signal_handler) == SIG_ERR) {
telem_log(LOG_ERR, "Error handling terminating signal\n");
}
// Gets the contents of the kernel ring buffer
bufp = (char *)malloc(buflen);
while (1) {
int bytes_read;
// 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));
malloc_trim(0);
memset(bufp, 0, buflen);
bytes_read = klogctl(SYSLOG_ACTION_READ, bufp, (int)buflen);
if (bytes_read < 0) {
bytes = klogctl(SYSLOG_ACTION_READ, bufp, (int)buflen);
if (bytes < 0) {
telem_log(LOG_ERR, "Cannot read contents of kernel ring buffer: %s\n", strerror(errno));
return 1;
}
klog_process_buffer(bufp, (size_t)bytes_read);
// Splits the buffer into separate lines with /0 terminating
split_buf_by_line(bufp, (size_t)bytes);
free(bufp);
}
// Not reached
free(bufp);
return 0;
}
+69 -95
View File
@@ -1,7 +1,7 @@
/*
* This program is part of the Clear Linux Project
*
* Copyright © 2015-2019 Intel Corporation
* 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
@@ -15,10 +15,11 @@
*/
#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>
@@ -26,54 +27,20 @@
#include "log.h"
#include "oops_parser.h"
#include "klog_scanner.h"
#include "telemetry.h"
#include "nica/nc-string.h"
static bool oops_processed = false;
#define MAX_OOPS_MSG_LENGTH 1024
#define FILE_PATH KERNELOOPSDIR "/kerneloopsXXXXXX"
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;
}
static int fd;
char filename[] = FILE_PATH;
int last_line_length;
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);
@@ -90,80 +57,87 @@ 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;
}
}
}
void klog_process_oops_msgs(struct oops_log_msg *msg)
int allocate_filespace()
{
size_t linelength, size;
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;
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);
strncpy(bp, line, linelength);
bp[linelength] = '\n';
done += linelength + 1;
bp = contents + done;
}
line[linelength] = '\n';
linelength++;
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]);
// 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);
}
#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");
}
free(contents);
oops_processed = true;
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);
}
}
void signal_handler_success(int signum)
bool get_oops_written(void)
{
exit(EXIT_SUCCESS);
return oops_written;
}
void signal_handler_fail(int signum)
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)
{
exit(EXIT_FAILURE);
}
+21 -17
View File
@@ -1,7 +1,7 @@
/*
* This program is part of the Clear Linux Project
*
* Copyright © 2015-2019 Intel Corporation
* 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
@@ -17,41 +17,45 @@
#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 Number of bytes read from ring buffer into bufp
* @param bytes Bytes read from ring buffer into bufp
*
*/
void split_buf_by_line(char *bufp, int bytes);
/**
* Process the oops message and send it to the backend
* 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
*
* @param msg Struct that contains oops message lines and linecount
*
*/
void klog_process_oops_msgs(struct oops_log_msg *msg);
void write_oops_to_file(struct oops_log_msg *msg);
/**
* Signal handler for cleanup purposes
*
* @param signum An integer for the signal type
*
*/
void signal_handler_fail(int signum);
void signal_handler_success(int signum);
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);
/* vi: set ts=8 sw=8 sts=4 et tw=80 cino=(0: */
+20 -7
View File
@@ -4,16 +4,10 @@ bin_PROGRAMS += \
%D%/telem-record-gen \
%D%/klogscanner \
%D%/pstoreprobe \
%D%/pythonprobe \
%D%/oopsprobe \
%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 = \
@@ -128,6 +122,25 @@ 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
+21 -18
View File
@@ -1,7 +1,7 @@
/*
* This program is part of the Clear Linux Project
*
* Copyright 2015-2019 Intel Corporation
* 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
@@ -152,9 +152,9 @@ struct oops_pattern oops_patterns_arr[] = {
},
};
static int oops_patterns_cnt = sizeof(oops_patterns_arr) / sizeof(struct oops_pattern);
int oops_patterns_cnt = sizeof(oops_patterns_arr) / sizeof(struct oops_pattern);
static char *skip_log_level(char *line)
char *skip_log_level(char *line)
{
char *start = line;
@@ -169,7 +169,7 @@ static char *skip_log_level(char *line)
return start;
}
static char *skip_timestamp(char *line)
char *skip_timestamp(char *line)
{
char *start = line;
@@ -184,7 +184,7 @@ static char *skip_timestamp(char *line)
return start;
}
static char *skip_space(char *line)
char *skip_space(char *line)
{
char *start = line;
@@ -194,7 +194,7 @@ static char *skip_space(char *line)
return start;
}
static char *skip_spaces(char *line)
char *skip_spaces(char *line)
{
char *start = line;
@@ -204,7 +204,7 @@ static char *skip_spaces(char *line)
return start;
}
static bool starts_with(const char *line, const char *line_end, const char *substr)
bool starts_with(const char *line, const char *line_end, const char *substr)
{
size_t substrlen = strlen(substr);
@@ -214,7 +214,7 @@ static bool starts_with(const char *line, const char *line_end, const char *subs
return false;
}
static bool str_starts_with_casei(const char *line, const char *substr)
bool str_starts_with_casei(const char *line, const char *substr)
{
size_t len = strlen(substr);
@@ -224,7 +224,7 @@ static bool str_starts_with_casei(const char *line, const char *substr)
return false;
}
static bool pattern_matches(char *line, char *line_end, struct oops_pattern *pattern)
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 @@ static bool pattern_matches(char *line, char *line_end, struct oops_pattern *pat
}
}
static void init_pattern_regex(void)
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 @@ static void init_pattern_regex(void)
}
}
static void free_pattern_regex(void)
void free_pattern_regex(void)
{
for (int i = 0; i < oops_patterns_cnt; i++) {
struct oops_pattern *pattern = &oops_patterns_arr[i];
@@ -261,6 +261,7 @@ 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) {
@@ -317,6 +318,7 @@ 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;
@@ -331,7 +333,7 @@ void oops_parser_init(oops_handler_t handler)
init_pattern_regex();
}
static void handle_msg_end(void)
void handle_msg_end(void)
{
for (int i = 0; i < oops_msg.length; i++) {
free(oops_msg.lines[i]);
@@ -424,6 +426,7 @@ void parse_single_line(char *line, size_t size)
}
}
}
}
struct stack_frame {
@@ -434,7 +437,7 @@ struct stack_frame {
struct stack_frame *next;
};
static void stack_frame_append(struct stack_frame **head, struct stack_frame **tail, char *start)
void stack_frame_append(struct stack_frame **head, struct stack_frame **tail, char *start)
{
/*
* Format is:
@@ -533,7 +536,7 @@ static void stack_frame_append(struct stack_frame **head, struct stack_frame **t
frame->module = "kernel";
}
static void stack_frame_free(struct stack_frame **head)
void stack_frame_free(struct stack_frame **head)
{
struct stack_frame *frame;
@@ -550,7 +553,7 @@ static 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
*/
static void parse_kernel_cpu_line(char *line, char **kernel_version, char **tainted)
void parse_kernel_cpu_line(char *line, char **kernel_version, char **tainted)
{
char *end = NULL;
@@ -623,7 +626,7 @@ static struct reg_s *reg_head = NULL;
*
*/
static void parse_registers(char *line)
void parse_registers(char *line)
{
uint64_t value;
struct reg_s *reg_entry = NULL;
@@ -736,7 +739,7 @@ static void parse_registers(char *line)
}
static void append_registers_to_bt(nc_string **backtrace)
void append_registers_to_bt(nc_string **backtrace)
{
struct reg_s *reg_entry = reg_head;
@@ -759,7 +762,7 @@ static void append_registers_to_bt(nc_string **backtrace)
}
}
static nc_string *parse_backtrace(struct oops_log_msg *msg)
nc_string *parse_backtrace(struct oops_log_msg *msg)
{
struct stack_frame *head = NULL, *tail = NULL, *elem = NULL;
//int in_stack_dump = 0;
+229
View File
@@ -0,0 +1,229 @@
/*
* 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
@@ -1,293 +0,0 @@
/*
* This program is part of the Clear Linux Project
*
* Copyright 2019 Intel Corporation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#define _GNU_SOURCE
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/inotify.h>
#include <fcntl.h>
#include <dirent.h>
#include <pwd.h>
#include <grp.h>
#include <getopt.h>
#include "log.h"
#include "telemetry.h"
#define PYTHON_TELEMETRY_DIR "/var/lib/telemetry/python"
/* We expect something like: 1.python-exception.3.N2Qy24 */
static bool validate_file_name(const char *filename, uint32_t *severity,
char** class, uint32_t *version)
{
unsigned int ver, sev;
char cls[PATH_MAX] = { 0 };
char rnd[PATH_MAX] = { 0 };
char *filename_base = basename(filename);
char *str_class = NULL;
if (!filename_base) {
telem_perror("basename failed");
return false;
}
if (sscanf(filename_base,"%u.%[^]0-9.].%u.%s", &ver, cls, &sev, rnd) == 4) {
if (asprintf(&str_class, "org.clearlinux/python/%s", cls) < 29) {
telem_log(LOG_ERR, "%s: invalid telemetry classification\n",
str_class);
return false;
}
*class = str_class;
*version = ver;
*severity = sev;
return true;
}
return false;
}
static char *read_file_into_buffer(const char *filename)
{
FILE *fp = NULL;
char *contents = NULL;
long sz;
size_t size, bytes_read;
fp = fopen(filename, "r");
if (fp == NULL) {
telem_log(LOG_ERR, "Failed to open Python telemetry file %s: %s\n",
filename, strerror(errno));
return NULL;
}
if (fseek(fp, 0, SEEK_END) == -1) {
telem_log(LOG_ERR, "Failed to seek file %s: %s\n", filename,
strerror(errno));
goto error_end;
}
sz = ftell(fp);
if (sz == -1L) {
telem_perror("ftell failed");
goto error_end;
}
if (sz == 0L) {
telem_perror("File is empty\n");
goto error_end;
}
if (fseek(fp, 0, SEEK_SET) < 0) {
telem_log(LOG_ERR, "fseek failed for file %s:%s\n", filename,
strerror(errno));
goto error_end;
}
size = (size_t)sz;
contents = calloc(sizeof(char), size);
if (!contents) {
telem_log(LOG_ERR, "Call to calloc failed\n");
goto error_end;
}
bytes_read = fread(contents, sizeof(char), size, fp);
if (bytes_read < size) {
telem_log(LOG_ERR, "Error while reading file %s:%s\n", filename,
strerror(errno));
goto error_end;
}
fclose(fp);
return contents;
error_end:
fclose(fp);
if (contents) {
free(contents);
}
return NULL;
}
static void send_data(char *contents, uint32_t severity, char *class,
uint32_t version)
{
struct telem_ref *handle = NULL;
int ret;
if ((ret = tm_create_record(&handle, severity, class, version)) < 0) {
telem_log(LOG_ERR, "Failed to create record: %s\n",
strerror(-ret));
return;
}
if ((ret = tm_set_payload(handle, contents)) < 0) {
telem_log(LOG_ERR, "Failed to set payload: %s\n",
strerror(-ret));
tm_free_record(handle);
return;
}
if ((ret = tm_send_record(handle)) < 0) {
telem_log(LOG_ERR, "Failed to send record: %s\n",
strerror(-ret));
}
tm_free_record(handle);
}
/* Send a valid file to the backend. */
static void deliver_payload(const char *filename)
{
uint32_t severity;
uint32_t version;
char *class = NULL;
char *contents = NULL;
if (validate_file_name(filename, &severity, &class, &version)) {
if ((contents = read_file_into_buffer(filename)) != NULL) {
send_data(contents, severity, class, version);
free(contents);
}
free(class);
}
/* Keep PYTHON_TELEMETRY_DIR empty. Delete the file. */
if (unlink(filename) != 0) {
telem_log(LOG_ERR, "Failed to unlink %s: %s\n", filename,
strerror(errno));
}
}
static void drop_privs(void)
{
uid_t euid;
euid = geteuid();
if (euid != 0) {
telem_log(LOG_DEBUG, "Not root; skipping privilege drop\n");
return;
}
struct passwd *pw;
pw = getpwnam("telemetry");
if (!pw) {
telem_log(LOG_ERR, "telemetry user not found\n");
exit(EXIT_FAILURE);
}
// The order is important here:
// change supplemental groups, our gid, and then our uid
if (initgroups(pw->pw_name, pw->pw_gid) != 0) {
telem_perror("Failed to set supplemental group list");
exit(EXIT_FAILURE);
}
if (setgid(pw->pw_gid) != 0) {
telem_perror("Failed to set GID");
exit(EXIT_FAILURE);
}
if (setuid(pw->pw_uid) != 0) {
telem_perror("Failed to set UID");
exit(EXIT_FAILURE);
}
assert(getuid() == pw->pw_uid);
assert(geteuid() == pw->pw_uid);
assert(getgid() == pw->pw_gid);
assert(getegid() == pw->pw_gid);
}
static void print_usage(char *prog)
{
printf("%s: Usage\n", prog);
printf(" -f, --config_file Specify a configuration file other than default\n");
printf(" -h, --help Display this help message\n");
printf(" -V, --version Print the program version\n");
}
int main(int argc, char **argv)
{
DIR *dir;
struct dirent *entry;
int c;
int opt_index = 0;
struct option opts[] = {
{ "config_file", 1, NULL, 'f' },
{ "help", 0, NULL, 'h' },
{ "version", 0, NULL, 'V' },
{ NULL, 0, NULL, 0 }
};
drop_privs();
while ((c = getopt_long(argc, argv, "f:hV", opts, &opt_index)) != -1) {
switch (c) {
case 'f':
if (tm_set_config_file(optarg) != 0) {
telem_log(LOG_ERR, "Configuration file"
" path not valid\n");
exit(EXIT_FAILURE);
}
break;
case 'h':
print_usage(argv[0]);
exit(EXIT_SUCCESS);
case 'V':
printf(PACKAGE_VERSION "\n");
exit(EXIT_SUCCESS);
case '?':
exit(EXIT_FAILURE);
}
}
if (chdir(PYTHON_TELEMETRY_DIR)) {
perror(PYTHON_TELEMETRY_DIR);
exit(EXIT_FAILURE);
}
/* Process all existing files */
dir = opendir(PYTHON_TELEMETRY_DIR);
if (!dir) {
perror(PYTHON_TELEMETRY_DIR);
exit(EXIT_FAILURE);
}
while (true) {
entry = readdir(dir);
if (!entry) {
break;
}
if (entry->d_name[0] == '.') {
continue;
}
deliver_payload(entry->d_name);
}
closedir(dir);
exit(EXIT_SUCCESS);
}
+29 -72
View File
@@ -39,13 +39,10 @@ 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' },
{ "echo", no_argument, 0, 'o' },
{ "no-post", no_argument, 0, 'n' },
{ "config-file", required_argument, 0, 'f' },
{ "version", no_argument, 0, 'V' },
{ "severity", required_argument, 0, 's' },
{ "class", required_argument, 0, 'c' },
@@ -65,15 +62,14 @@ 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 (required)\n");
printf(" -p, --payload Record body (max size = 8k) (required)\n");
printf(" -c, --class Classification level_1/level_2/level_3\n");
printf(" -p, --payload Record body (max size = 8k)\n");
printf(" -P, --payload-file File to read payload from\n");
printf(" -R, --record-version Version number for format of payload (default 1)\n");
printf(" -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");
}
@@ -98,7 +94,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:on", prog_opts, NULL)) != -1) {
while ((opt = getopt_long(argc, argv, "hc:Vs:c:p:P:R:e:", prog_opts, NULL)) != -1) {
switch (opt) {
case 'h':
print_help();
@@ -153,12 +149,6 @@ int parse_options(int argc, char **argv)
goto fail;
}
break;
case 'o':
opt_echo = true;
break;
case 'n':
opt_nopost = true;
break;
}
}
@@ -228,13 +218,13 @@ int allocate_payload_buffer(char **payload)
{
int ret = 0;
*payload = (char *)malloc(MAX_PAYLOAD_LENGTH);
*payload = (char *)malloc(MAX_PAYLOAD_SIZE);
if (*payload == NULL) {
goto out1;
}
*payload = memset(*payload, 0, MAX_PAYLOAD_LENGTH);
*payload = memset(*payload, 0, MAX_PAYLOAD_SIZE);
ret = 1;
out1:
@@ -256,7 +246,7 @@ int get_payload_from_file(char **payload)
}
bytes_in = fread(*payload, 1,
MAX_PAYLOAD_LENGTH - 1, fp);
MAX_PAYLOAD_SIZE - 1, fp);
/* if fread fails */
if (bytes_in == 0 && ferror(fp) != 0) {
@@ -277,8 +267,8 @@ void get_payload_from_opt(char **payload)
size_t len = 0;
len = strlen(opt_payload);
if (len >= MAX_PAYLOAD_LENGTH) {
len = MAX_PAYLOAD_LENGTH - 1;
if (len >= MAX_PAYLOAD_SIZE) {
len = MAX_PAYLOAD_SIZE - 1;
}
strncpy(*payload, opt_payload, len);
}
@@ -288,9 +278,9 @@ void get_payload_from_stdin(char **payload)
size_t bytes_in = 0;
int c;
bytes_in = fread(*payload, 1, MAX_PAYLOAD_LENGTH - 1, stdin);
bytes_in = fread(*payload, 1, MAX_PAYLOAD_SIZE - 1, stdin);
if (bytes_in == MAX_PAYLOAD_LENGTH - 1) {
if (bytes_in == MAX_PAYLOAD_SIZE - 1) {
/* Throw away the rest of stdin */
while (EOF != (c = fgetc(stdin))) {
continue;
@@ -332,59 +322,31 @@ out1:
return ret;
}
int instanciate_record(struct telem_ref **t_ref, char *payload)
{
int ret = 0;
if ((ret = tm_create_record(t_ref, (uint32_t)severity,
opt_class, payload_version)) < 0) {
goto out1;
}
if ((ret = opt_event_id && tm_set_event_id(*t_ref, opt_event_id)) < 0) {
goto out1;
}
if ((ret = tm_set_payload(*t_ref, payload)) < 0) {
goto out1;
}
out1:
return ret;
}
int send_record(char *payload)
{
struct telem_ref *t_ref = NULL;
struct telem_ref *t_ref;
int ret = 0;
if ((ret = instanciate_record(&t_ref, payload)) < 0) {
goto out;
if (tm_create_record(&t_ref, (uint32_t)severity,
opt_class, payload_version) < 0) {
goto out1;
}
if ((ret = tm_send_record(t_ref)) < 0) {
goto out;
if (opt_event_id && tm_set_event_id(t_ref, opt_event_id) < 0) {
goto out2;
}
out:
if (tm_set_payload(t_ref, payload) < 0) {
goto out2;
}
if (tm_send_record(t_ref) >= 0) {
ret = 1;
}
out2:
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);
}
out1:
return ret;
}
@@ -405,11 +367,7 @@ int main(int argc, char **argv)
goto fail;
}
if (opt_echo == true) {
print_record(payload);
}
if (opt_nopost == false && send_record(payload) != 0) {
if (!send_record(payload)) {
goto fail;
}
@@ -419,7 +377,6 @@ 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 "telempostdaemon.h"
#include "telemdaemon.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])) {
if (get_header(line, header_name, &headers[i], strlen(header_name))) {
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);
*post_succeeded = post_record_http(headers, payload, false);
if (*post_succeeded) {
unlink(record_path);
}
+396 -11
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,7 +43,32 @@ void initialize_daemon(TelemDaemon *daemon)
daemon->nfds = 0;
daemon->pollfds = NULL;
daemon->client_head = head;
daemon->machine_id_override = NULL;
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();
}
client *add_client(client_list_head *client_head, int fd)
@@ -247,18 +272,42 @@ 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;
char *recordpath = NULL;
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;
header_size = *(uint32_t *)cl->buf;
message_size = cl->size - header_size;
@@ -272,7 +321,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])) {
if (get_header(tok, header_name, &headers[i], strlen(header_name))) {
if (strcmp(header_name, TM_MACHINE_ID_STR) == 0) {
machine_id_replace(&headers[i], daemon->machine_id_override);
}
@@ -286,15 +335,83 @@ void process_record(TelemDaemon *daemon, client *cl)
/* TODO : check if the body is within the limits. */
body = msg + header_size;
/* 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");
/* 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");
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++)
@@ -302,6 +419,274 @@ 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);
+143 -1
View File
@@ -22,6 +22,7 @@
#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*/)
@@ -54,6 +55,26 @@ 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;
@@ -64,6 +85,21 @@ 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.
*
@@ -146,6 +182,113 @@ 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
*
@@ -167,5 +310,4 @@ 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 (telemprobd), and populated by the daemon
* managed by the telemetrics daemon (telemd), 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 - 3] = '\0';
model_name[model_str_len - 2] = '\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;
}
int tm_set_config_file(const char *c_file)
void tm_set_config_file(char *c_file)
{
return set_config_file(c_file);
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 telemprobd.
* Write nbytes from buf to fd. Used to send records to telemd.
*
* @param fd Socket fd obtained from tm_get_socket.
* @param buf Data to be written to the socket.
+5 -8
View File
@@ -31,10 +31,7 @@
* - a library, libtelemetry, that telemetrics probes use to create telemetrics
* records and send them to the daemon for further processing.
*
* - telemprobd, a daemon that handles communication with probes and forwards
* records to a second daemon.
*
* - telempostd, this daemon sends telemetry records to a telemetrics server
* - a daemon, telemd, that prepares the records to send to a telemetrics server
* (not included in this source tree), or spools the records on disk in case
* it's unable to successfully deliver them.
*
@@ -58,11 +55,11 @@ struct telem_ref {
/**
* Set the configuration file name to use
*
* @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.
* @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.
*/
int tm_set_config_file(const char *c_file);
void tm_set_config_file(char *c_file);
/**
* Create a new telemetrics record
-755
View File
@@ -1,755 +0,0 @@
/*
* This program is part of the Clear Linux Project
*
* Copyright 2018 Intel Corporation
*
* This program is free software; you can redistribute it and/or modify it under
* the terms and conditions of the GNU Lesser General Public License, as
* published by the Free Software Foundation; either version 2.1 of the License,
* or (at your option) any later version.
*
* This program is distributed in the hope it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*/
#define _GNU_SOURCE
#include <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
@@ -1,135 +0,0 @@
/*
* This program is part of the Clear Linux Project
*
* Copyright 2018 Intel Corporation
*
* This program is free software; you can redistribute it and/or modify it under
* the terms and conditions of the GNU Lesser General Public License, as
* published by the Free Software Foundation; either version 2.1 of the License,
* or (at your option) any later version.
*
* This program is distributed in the hope it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*/
#define 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: */
+1 -3
View File
@@ -34,10 +34,8 @@
#include "common.h"
#include "util.h"
bool get_header(const char *haystack, const char *needle, char **line)
bool get_header(const char *haystack, const char *needle, char **line, size_t len)
{
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);
bool get_header(const char *haystack, const char *needle, char **line, size_t len);
/* 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], 180);
ck_assert_int_eq(config.intValues[CONF_SPOOL_PROCESS_TIME], 900);
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(), 180);
ck_assert_int_eq(spool_process_time_config(), 900);
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);
+316 -2
View File
@@ -26,6 +26,7 @@
#include "common.h"
TelemDaemon tdaemon;
static bool post_was_called = false;
char *get_serialized_record(char *headers, char *post_body, size_t *record_size)
{
@@ -53,6 +54,14 @@ 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";
@@ -67,6 +76,12 @@ 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
@@ -254,10 +269,12 @@ 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");
@@ -290,12 +307,14 @@ 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);
@@ -322,12 +341,14 @@ 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);
@@ -335,13 +356,296 @@ 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("probd");
Suite *s = suite_create("daemon");
// Individual unit tests are added to "test cases"
TCase *t = tcase_create("probd");
TCase *t = tcase_create("daemon");
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);
@@ -351,6 +655,16 @@ 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);
+4 -13
View File
@@ -20,17 +20,11 @@
#include "common.h"
#include "journal/journal.h"
static char *journal_file = "journal.txt";
static char *journal_print_file = "journal.print.txt";
static struct TelemJournal *journal = NULL;
static char *journal_file = TESTOOPSDIR "/journal.txt";
static char *eid = "00007766547776eb7fc478eb0eb43e43";
static int K = 20;
void teardown(void)
{
remove(journal_file);
}
START_TEST(check_open_journal)
{
struct TelemJournal *j = open_journal(journal_file);
@@ -114,7 +108,6 @@ void new_entry_teardown(void)
close_journal(journal);
journal = NULL;
}
remove(journal_file);
}
void insert_n_records(int n, struct TelemJournal *j)
@@ -135,7 +128,7 @@ START_TEST(check_journal_file_prune)
insert_n_records(j->record_count_limit * 2, j);
ck_assert_int_gt(j->record_count, j->record_count_limit);
rc = prune_journal(j, "./");
rc = prune_journal(j, TESTOOPSDIR);
ck_assert(rc == 0);
// Record count should always be below the limit + hysteresis
ck_assert_int_lt(j->record_count, j->record_count_limit + DEVIATION);
@@ -146,12 +139,13 @@ END_TEST
void journal_entry_setup(void)
{
int result = 0;
const char *journal_print = TESTOOPSDIR "/journal.print.txt";
if (journal) {
close_journal(journal);
}
journal = open_journal(journal_print_file);
journal = open_journal(journal_print);
insert_n_records(K, journal);
/* Insert recors with specific values */
@@ -194,7 +188,6 @@ END_TEST
void journal_entry_teardown(void)
{
close_journal(journal);
remove(journal_print_file);
}
Suite *config_suite(void)
@@ -204,7 +197,6 @@ Suite *config_suite(void)
// Individual unit tests are added to "test cases"
TCase *t = tcase_create("journal");
tcase_add_unchecked_fixture(t, NULL, teardown);
tcase_add_test(t, check_open_journal);
tcase_add_test(t, check_unsuccessful_open_journal);
suite_add_tcase(s, t);
@@ -219,7 +211,6 @@ Suite *config_suite(void)
suite_add_tcase(s, t);
t = tcase_create("prunning journal");
tcase_add_unchecked_fixture(t, NULL, teardown);
tcase_add_test(t, check_journal_file_prune);
suite_add_tcase(s, t);
+1
View File
@@ -190,3 +190,4 @@ int main(void)
}
/* vi: set ts=8 sw=8 sts=4 et tw=80 cino=(0: */
-443
View File
@@ -1,443 +0,0 @@
/*
* This program is part of the Clear Linux Project
*
* Copyright 2018 Intel Corporation
*
* This program is free software; you can redistribute it and/or modify it under
* the terms and conditions of the GNU Lesser General Public License, as
* published by the Free Software Foundation; either version 2.1 of the License,
* or (at your option) any later version.
*
* This program is distributed in the hope it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*/
#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: */
+21
View File
@@ -40,6 +40,27 @@ 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;
+9 -49
View File
@@ -12,8 +12,7 @@ TESTS = $(check_PROGRAMS) $(dist_check_SCRIPTS)
check_PROGRAMS = \
%D%/check_config \
%D%/check_probd \
%D%/check_postd \
%D%/check_daemon \
%D%/check_probes \
%D%/check_ncb64 \
%D%/check_journal \
@@ -33,63 +32,27 @@ dist_check_SCRIPTS = \
@CHECK_LIBS@ \
$(top_builddir)/src/libtelem-shared.la
%C%_check_probd_SOURCES = \
%D%/check_probd.c \
%C%_check_daemon_SOURCES = \
%D%/check_daemon.c \
src/telemdaemon.c \
src/telemdaemon.h \
src/iorecord.h \
src/iorecord.c \
src/journal/journal.c \
src/journal/journal.h
%C%_check_probd_CFLAGS = \
%C%_check_daemon_CFLAGS = \
$(AM_CFLAGS) \
@CHECK_CFLAGS@ \
@CURL_CFLAGS@
%C%_check_probd_LDADD = \
%C%_check_daemon_LDADD = \
@CHECK_LIBS@ \
@CURL_LIBS@ \
$(top_builddir)/src/libtelem-shared.la
if LOG_SYSTEMD
if HAVE_SYSTEMD_JOURNAL
%C%_check_probd_CFLAGS += \
%C%_check_daemon_CFLAGS += \
$(SYSTEMD_JOURNAL_CFLAGS)
%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 += \
%C%_check_daemon_LDADD += \
$(SYSTEMD_JOURNAL_LIBS)
endif
endif
@@ -136,7 +99,6 @@ EXTRA_DIST += \
@CHECK_CFLAGS@
%C%_check_probes_LDADD = \
@CHECK_LIBS@ \
$(top_builddir)/src/libtelemetry.la \
$(top_builddir)/src/libtelem-shared.la
if LOG_SYSTEMD
@@ -169,6 +131,7 @@ EXTRA_DIST += \
%C%_check_journal_SOURCES = \
%D%/check_journal.c \
src/journal/journal.h \
src/journal/journal.c \
src/util.h \
src/util.c
@@ -182,10 +145,7 @@ EXTRA_DIST += \
%C%_check_libtelemetry_SOURCES = \
%D%/check_libtelemetry.c \
src/configuration.h \
src/util.h \
src/nica/hashmap.h \
src/nica/inifile.h
src/telemetry.c
%C%_check_libtelemetry_CFLAGS = \
$(AM_CFLAGS) \
-16
View File
@@ -1,16 +0,0 @@
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
@@ -1,10 +0,0 @@
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
@@ -1 +0,0 @@
test