Coverage Report

Created: 2026-04-01 07:08

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ntopng/src/TimeseriesExporter.cpp
Line
Count
Source
1
/*
2
 *
3
 * (C) 2018-26 - ntop.org
4
 *
5
 *
6
 * This program is free software; you can redistribute it and/or modify
7
 * it under the terms of the GNU General Public License as published by
8
 * the Free Software Foundation; either version 3 of the License, or
9
 * (at your option) any later version.
10
 *
11
 * This program is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU General Public License
17
 * along with this program; if not, write to the Free Software Foundation,
18
 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19
 *
20
 */
21
22
#include "ntop_includes.h"
23
24
/* ******************************************************* */
25
26
0
TimeseriesExporter::TimeseriesExporter(NetworkInterface* _if) {
27
0
  if (trace_new_delete)
28
0
    ntop->getTrace()->traceEvent(TRACE_NORMAL, "[new] %s", __FILE__);
29
0
  iface = _if;
30
0
}
31
32
/* ******************************************************* */
33
34
0
TimeseriesExporter::~TimeseriesExporter() {}
35
36
/* ******************************************************* */
37
38
0
bool TimeseriesExporter::is_table_empty(lua_State* L, int index) {
39
0
  lua_pushnil(L);
40
41
0
  if (lua_next(L, index)) {
42
0
    lua_pop(L, 1);
43
0
    return (false);
44
0
  }
45
46
0
  return (true);
47
0
}
48
49
/* ******************************************************* */
50
51
/* NOTE: outbuf and unescaped buffers must not overlap.
52
   Need to escape spaces at least.
53
54
   See
55
   https://docs.influxdata.com/influxdb/v1.7/write_protocols/line_protocol_tutorial/#special-characters
56
*/
57
int TimeseriesExporter::escape_spaces(char* buf, int buf_len,
58
0
                                      const char* unescaped) {
59
0
  int cur_len = 0;
60
61
0
  while (*unescaped) {
62
0
    if (cur_len >= buf_len - 2) goto influx_escape_char_err;
63
64
0
    switch (*unescaped) {
65
0
      case ' ':
66
0
        *buf++ = '\\';
67
0
        cur_len++;
68
        /* No break */
69
0
      default:
70
0
        *buf++ = *unescaped++;
71
0
        cur_len++;
72
0
        break;
73
0
    }
74
0
  }
75
76
0
  *buf = '\0';
77
0
  return cur_len;
78
79
0
influx_escape_char_err:
80
0
  *(buf - cur_len) = '\0';
81
0
  return -1;
82
0
}
83
84
/* ******************************************************* */
85
86
int TimeseriesExporter::line_protocol_concat_table_fields(
87
    lua_State* L, int index, char* buf, int buf_len,
88
0
    int (*escape_fn)(char* outbuf, int outlen, const char* orig)) {
89
0
  bool first = true;
90
0
  char val_buf[128];
91
0
  int cur_buf_len = 0, n;
92
0
  bool write_ok;
93
94
  // table traversal from https://www.lua.org/ftp/refman-5.0.pdf
95
0
  lua_pushnil(L);
96
97
0
  while (lua_next(L, index) != 0) {
98
0
    const char* s = lua_tostring(L, -1);
99
100
0
    write_ok = false;
101
102
0
    if (escape_fn)
103
0
      n = escape_fn(val_buf, sizeof(val_buf), s);
104
0
    else
105
0
      n = snprintf(val_buf, sizeof(val_buf), "%s", s);
106
107
0
    if (n > 0 && n < (int)sizeof(val_buf)) {
108
0
      n = snprintf(buf + cur_buf_len, buf_len - cur_buf_len, "%s%s=%s",
109
0
                   first ? "" : ",", lua_tostring(L, -2), val_buf);
110
111
0
      if (n > 0 && n < buf_len - cur_buf_len) {
112
0
        write_ok = true;
113
0
        cur_buf_len += n;
114
0
        if (first) first = false;
115
0
      }
116
0
    }
117
118
0
    lua_pop(L, 1);
119
0
    if (!write_ok) goto line_protocol_concat_table_fields_err;
120
0
  }
121
122
0
  return cur_buf_len;
123
124
0
line_protocol_concat_table_fields_err:
125
0
  if (buf_len) buf[0] = '\0';
126
127
0
  return -1;
128
0
}
129
130
/* ******************************************************* */
131
132
int TimeseriesExporter::line_protocol_write_line(
133
    lua_State* vm, char* dst_line, int dst_line_len,
134
0
    int (*escape_fn)(char* outbuf, int outlen, const char* orig)) {
135
0
  char* schema;
136
0
  time_t tstamp;
137
0
  int cur_line_len = 0, n;
138
139
0
  if (ntop_lua_check(vm, __FUNCTION__, 1, LUA_TSTRING) != CONST_LUA_OK)
140
0
    return -1;
141
0
  schema = (char*)lua_tostring(vm, 1);
142
143
0
  if (ntop_lua_check(vm, __FUNCTION__, 2, LUA_TNUMBER) != CONST_LUA_OK)
144
0
    return -1;
145
0
  tstamp = (time_t)lua_tonumber(vm, 2);
146
147
0
  if (ntop_lua_check(vm, __FUNCTION__, 3, LUA_TTABLE) != CONST_LUA_OK)
148
0
    return -1;
149
0
  if (ntop_lua_check(vm, __FUNCTION__, 4, LUA_TTABLE) != CONST_LUA_OK)
150
0
    return -1;
151
152
  /* A line of the protocol is: "iface:traffic,ifid=0 bytes=0 1539358699\n" */
153
154
  /* measurement name (with a comma if no tags are found) */
155
0
  n = snprintf(dst_line, dst_line_len, is_table_empty(vm, 3) ? "%s" : "%s,",
156
0
               schema);
157
0
  if (n < 0 || n >= dst_line_len)
158
0
    goto line_protocol_write_line_err;
159
0
  else
160
0
    cur_line_len += n;
161
162
  /* tags */
163
0
  n = line_protocol_concat_table_fields(vm, 3, dst_line + cur_line_len,
164
0
                                        dst_line_len - cur_line_len,
165
0
                                        escape_fn);  // tags
166
0
  if (n < 0 || n >= dst_line_len - cur_line_len)
167
0
    goto line_protocol_write_line_err;
168
0
  else
169
0
    cur_line_len += n;
170
171
  /* space to separate tags and metrics */
172
0
  n = snprintf(dst_line + cur_line_len, dst_line_len - cur_line_len, " ");
173
0
  if (n < 0 || n >= dst_line_len - cur_line_len)
174
0
    goto line_protocol_write_line_err;
175
0
  else
176
0
    cur_line_len += n;
177
178
  /* metrics */
179
0
  n = line_protocol_concat_table_fields(vm, 4, dst_line + cur_line_len,
180
0
                                        dst_line_len - cur_line_len,
181
0
                                        escape_fn);  // metrics
182
0
  if (n < 0 || n >= dst_line_len - cur_line_len)
183
0
    goto line_protocol_write_line_err;
184
0
  else
185
0
    cur_line_len += n;
186
187
  /* timestamp (in seconds, not nanoseconds) and a \n */
188
0
  n = snprintf(dst_line + cur_line_len, dst_line_len - cur_line_len, " %lu\n",
189
0
               tstamp);
190
0
  if (n < 0 || n >= dst_line_len - cur_line_len)
191
0
    goto line_protocol_write_line_err;
192
0
  else
193
0
    cur_line_len += n;
194
195
0
  return cur_line_len;
196
197
0
line_protocol_write_line_err:
198
0
  if (dst_line_len) dst_line[0] = '\0';
199
200
0
  return -1;
201
0
}