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