@cryptotaxi247 / netdata-1 / commits / b6aeb7e3d

CUPS plugin (#5188)

* Implement a CUPS collector plugin * Compile cups.plugin only if enabled * Fix build with CFLAGS -O1 -ggdb -Wall -Wextra -Wformat-signedness -fstack-protector-all -DNETDATA_INTERNAL_CHECKS=1 -D_FORTIFY_SOURCE=2 -DNETDATA_VERIFY_LOCKS=1 * cups.plugin check for all used functions * Use cups-config to configure cups compiler flags * Do not quit if cups-config is not installed * Fix compiler warning collectors/cups.plugin/cups_plugin.c:359:27: warning: format '%d' expects argument of type 'int', but argument 6 has type 'unsigned int' [-Wformat=] * Add cups.plugin to toc and overview documentation * cups.plugin fix plugin doc layout * cups.plugin: Add prerequisites doc * cups.plugin: Fix error if cups is not installed

Simon Nagl committed Feb 4, 2019 at 15:33 UTC b6aeb7e3d98268c16d923c08a06795390c75a191
13 files changed +636 -1
.gitignore
+3
@@ -47,6 +47,9 @@ apps.plugin
47 freeipmi.plugin
48 !freeipmi.plugin/
49
50 +cups.plugin
51 +!cups.plugin/
52 +
53 cgroup-network
54 !cgroup-network/
55
Makefile.am
+15
@@ -105,6 +105,7 @@ AM_CFLAGS = \
105 $(OPTIONAL_UUID_CFLAGS) \
106 $(OPTIONAL_LIBCAP_LIBS) \
107 $(OPTIONAL_IPMIMONITORING_CFLAGS) \
108 + $(OPTIONAL_CUPS_CFLAGS) \
109 $(NULL)
110
111 sbin_PROGRAMS =
@@ -212,6 +213,11 @@ FREEIPMI_PLUGIN_FILES = \
213 $(LIBNETDATA_FILES) \
214 $(NULL)
215
216 +CUPS_PLUGIN_FILES = \
217 + collectors/cups.plugin/cups_plugin.c \
218 + $(LIBNETDATA_FILES) \
219 + $(NULL)
220 +
221 NFACCT_PLUGIN_FILES = \
222 collectors/nfacct.plugin/plugin_nfacct.c \
223 collectors/nfacct.plugin/plugin_nfacct.h \
@@ -488,3 +494,12 @@ if ENABLE_PLUGIN_FREEIPMI
494 $(OPTIONAL_IPMIMONITORING_LIBS) \
495 $(NULL)
496 endif
497 +
498 +if ENABLE_PLUGIN_CUPS
499 + plugins_PROGRAMS += cups.plugin
500 + cups_plugin_SOURCES = $(CUPS_PLUGIN_FILES)
501 + cups_plugin_LDADD = \
502 + $(NETDATA_COMMON_LIBS) \
503 + $(OPTIONAL_CUPS_LIBS) \
504 + $(NULL)
505 +endif
collectors/Makefile.am
+1
@@ -8,6 +8,7 @@ SUBDIRS = \
8 cgroups.plugin \
9 charts.d.plugin \
10 checks.plugin \
11 + cups.plugin \
12 diskspace.plugin \
13 fping.plugin \
14 freebsd.plugin \
collectors/README.md
+1
@@ -27,6 +27,7 @@ plugin|lang|O/S|runs as|modular|description
27 [cgroups.plugin](cgroups.plugin/)|`C`|linux|internal|-|collects resource usage of **Containers**, libvirt **VMs** and **systemd services**, on Linux systems
28 [charts.d.plugin](charts.d.plugin/)|`BASH` v4+|any|external|yes|a **plugin orchestrator** for data collection modules written in `BASH` v4+.
29 [checks.plugin](checks.plugin/)|`C`|any|internal|-|a debugging plugin (by default it is disabled)
30 +[cups.plugin](cups.plugin/)|`C`|any|external|-|monitors **CUPS**
31 [diskspace.plugin](diskspace.plugin/)|`C`|linux|internal|-|collects disk space usage metrics on Linux mount points
32 [fping.plugin](fping.plugin/)|`C`|any|external|-|measures network latency, jitter and packet loss between the monitored node and any number of remote network end points.
33 [freebsd.plugin](freebsd.plugin/)|`C`|freebsd|internal|yes|collects resource usage and performance data on FreeBSD systems
collectors/cups.plugin/Makefile.am new
+9
@@ -0,0 +1,9 @@
1 +# SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +AUTOMAKE_OPTIONS = subdir-objects
4 +
5 +MAINTAINERCLEANFILES = $(srcdir)/Makefile.in
6 +
7 +dist_noinst_DATA = \
8 + README.md \
9 + $(NULL)
collectors/cups.plugin/README.md new
+49
@@ -0,0 +1,49 @@
1 +# cups.plugin
2 +
3 +`cups.plugin` collects Common Unix Printing System (CUPS) metrics.
4 +
5 +## Prerequisites
6 +
7 +This plugin needs a running local CUPS daemon (`cupsd`). This plugin does not need any configuration.
8 +
9 +## Charts
10 +
11 +`cups.plugin` provides one common section `destinations` and one section per destination.
12 +
13 +> Destinations in CUPS represent individual printers or classes (collections or pools) of printers (https://www.cups.org/doc/cupspm.html#working-with-destinations)
14 +
15 +The section `server` provides these charts:
16 +
17 +1. **destinations by state**
18 + * idle
19 + * printing
20 + * stopped
21 +
22 +2. **destinations by options**
23 + * total
24 + * accepting jobs
25 + * shared
26 +
27 +3. **total job number by status**
28 + * pending
29 + * processing
30 + * held
31 +
32 +4. **total job size by status**
33 + * pending
34 + * processing
35 + * held
36 +
37 +For each destination the plugin provides these charts:
38 +
39 +1. **job number by status**
40 + * pending
41 + * held
42 + * processing
43 +
44 +3. **job size by status**
45 + * pending
46 + * held
47 + * processing
48 +
49 +At the moment only job status pending, processing, and held are reported because we do not have a method to collect stopped, canceled, aborted and completed jobs which scales.
collectors/cups.plugin/cups_plugin.c new
+442
@@ -0,0 +1,442 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +/*
4 + * netdata cups.plugin
5 + * (C) Copyright 2017-2018 Simon Nagl <simon.nagl@gmx.de>
6 + * Released under GPL v3+
7 + */
8 +
9 +#include "../../libnetdata/libnetdata.h"
10 +#include <limits.h>
11 +
12 +// callback required by fatal()
13 +void netdata_cleanup_and_exit(int ret) {
14 + exit(ret);
15 +}
16 +
17 +// callbacks required by popen()
18 +void signals_block(void) {};
19 +void signals_unblock(void) {};
20 +void signals_reset(void) {};
21 +
22 +// callback required by eval()
23 +int health_variable_lookup(const char *variable, uint32_t hash, struct rrdcalc *rc, calculated_number *result) {
24 + (void)variable;
25 + (void)hash;
26 + (void)rc;
27 + (void)result;
28 + return 0;
29 +};
30 +
31 +// required by get_system_cpus()
32 +char *netdata_configured_host_prefix = "";
33 +
34 +// Variables
35 +
36 +static int debug = 0;
37 +
38 +static int netdata_update_every = 1;
39 +static int netdata_priority = 100004;
40 +
41 +
42 +#ifdef HAVE_CUPS
43 +#include <cups/cups.h>
44 +
45 +http_t *http; // connection to the cups daemon
46 +
47 +/*
48 + * Used to aggregate job metrics for a destination (and all destianations).
49 + */
50 +struct job_metrics {
51 + int is_collected; // flag if this was collected in the current cycle
52 +
53 + int num_pending;
54 + int num_processing;
55 + int num_held;
56 +
57 + int size_pending; // in kilobyte
58 + int size_processing; // in kilobyte
59 + int size_held; // in kilobyte
60 +};
61 +DICTIONARY *dict_dest_job_metrics = NULL;
62 +struct job_metrics global_job_metrics;
63 +
64 +int num_dest_total;
65 +int num_dest_accepting_jobs;
66 +int num_dest_shared;
67 +
68 +int num_dest_idle;
69 +int num_dest_printing;
70 +int num_dest_stopped;
71 +
72 +void print_help() {
73 + fprintf(stderr,
74 + "\n"
75 + "netdata cups.plugin %s\n"
76 + "\n"
77 + "Copyright (C) 2017-2018 Simon Nagl <simon.nagl@gmx.de>\n"
78 + "Released under GNU General Public License v3+.\n"
79 + "All rights reserved.\n"
80 + "\n"
81 + "This program is a data collector plugin for netdata.\n"
82 + "\n"
83 + "SYNOPSIS: cups.plugin [-d][-h][-v] COLLECTION_FREQUENCY\n"
84 + "\n"
85 + "Options:"
86 + "\n"
87 + " COLLECTION_FREQUENCY data collection frequency in seconds\n"
88 + "\n"
89 + " -d enable verbose output\n"
90 + " default: disabled\n"
91 + "\n"
92 + " -v print version and exit\n"
93 + "\n"
94 + " -h print this message and exit\n"
95 + "\n",
96 + VERSION);
97 +}
98 +
99 +void parse_command_line(int argc, char **argv) {
100 + int i;
101 + int freq = 0;
102 + int update_every_found = 0;
103 + for (i = 1; i < argc; i++) {
104 + if (isdigit(*argv[i]) && !update_every_found) {
105 + int n = str2i(argv[i]);
106 + if (n > 0 && n < 86400) {
107 + freq = n;
108 + continue;
109 + }
110 + } else if (strcmp("-v", argv[i]) == 0) {
111 + printf("cups.plugin %s\n", VERSION);
112 + exit(0);
113 + } else if (strcmp("-d", argv[i]) == 0) {
114 + debug = 1;
115 + continue;
116 + } else if (strcmp("-h", argv[i]) == 0) {
117 + print_help();
118 + exit(0);
119 + }
120 +
121 + print_help();
122 + exit(1);
123 + }
124 +
125 + if (freq >= netdata_update_every) {
126 + netdata_update_every = freq;
127 + } else if (freq) {
128 + error("update frequency %d seconds is too small for CUPS. Using %d.", freq, netdata_update_every);
129 + }
130 +}
131 +
132 +int reset_job_metrics(void *entry, void *data) {
133 + (void)data;
134 +
135 + struct job_metrics *jm = (struct job_metrics *)entry;
136 +
137 + jm->is_collected = 0;
138 + jm->num_held = 0;
139 + jm->num_pending = 0;
140 + jm->num_processing = 0;
141 + jm->size_held = 0;
142 + jm->size_pending = 0;
143 + jm->size_processing = 0;
144 +
145 + return 0;
146 +}
147 +
148 +struct job_metrics *get_job_metrics(char *dest) {
149 + struct job_metrics *jm = dictionary_get(dict_dest_job_metrics, dest);
150 +
151 + if (unlikely(!jm)) {
152 + struct job_metrics new_job_metrics;
153 + reset_job_metrics(&new_job_metrics, NULL);
154 + jm = dictionary_set(dict_dest_job_metrics, dest, &new_job_metrics, sizeof(struct job_metrics));
155 +
156 + printf("CHART cups.job_num_%s '' 'Active job number of destination %s' jobs '%s' job_num stacked %i %i\n", dest, dest, dest, netdata_priority++, netdata_update_every);
157 + printf("DIMENSION pending '' absolute 1 1\n");
158 + printf("DIMENSION held '' absolute 1 1\n");
159 + printf("DIMENSION processing '' absolute 1 1\n");
160 +
161 + printf("CHART cups.job_size_%s '' 'Active job size of destination %s' KB '%s' job_size stacked %i %i\n", dest, dest, dest, netdata_priority++, netdata_update_every);
162 + printf("DIMENSION pending '' absolute 1 1\n");
163 + printf("DIMENSION held '' absolute 1 1\n");
164 + printf("DIMENSION processing '' absolute 1 1\n");
165 + };
166 + return jm;
167 +}
168 +
169 +int collect_job_metrics(char *name, void *entry, void *data) {
170 + (void)data;
171 +
172 + struct job_metrics *jm = (struct job_metrics *)entry;
173 +
174 + if (jm->is_collected) {
175 + printf(
176 + "BEGIN cups.job_num_%s\n"
177 + "SET pending = %d\n"
178 + "SET held = %d\n"
179 + "SET processing = %d\n"
180 + "END\n",
181 + name, jm->num_pending, jm->num_held, jm->num_processing);
182 + printf(
183 + "BEGIN cups.job_size_%s\n"
184 + "SET pending = %d\n"
185 + "SET held = %d\n"
186 + "SET processing = %d\n"
187 + "END\n",
188 + name, jm->size_pending, jm->size_held, jm->size_processing);
189 + } else {
190 + printf("CHART cups.job_num_%s '' 'Active job number of destination %s' jobs '%s' job_num stacked 1 %i 'obsolete'\n", name, name, name, netdata_update_every);
191 + printf("DIMENSION pending '' absolute 1 1\n");
192 + printf("DIMENSION held '' absolute 1 1\n");
193 + printf("DIMENSION processing '' absolute 1 1\n");
194 +
195 + printf("CHART cups.job_size_%s '' 'Active job size of destination %s' KB '%s' job_size stacked 1 %i 'obsolete'\n", name, name, name, netdata_update_every);
196 + printf("DIMENSION pending '' absolute 1 1\n");
197 + printf("DIMENSION held '' absolute 1 1\n");
198 + printf("DIMENSION processing '' absolute 1 1\n");
199 + dictionary_del(dict_dest_job_metrics, name);
200 + }
201 +
202 + return 0;
203 +}
204 +
205 +void reset_metrics() {
206 + num_dest_total = 0;
207 + num_dest_accepting_jobs = 0;
208 + num_dest_shared = 0;
209 +
210 + num_dest_idle = 0;
211 + num_dest_printing = 0;
212 + num_dest_stopped = 0;
213 +
214 + reset_job_metrics(&global_job_metrics, NULL);
215 + dictionary_get_all(dict_dest_job_metrics, reset_job_metrics, NULL);
216 +}
217 +
218 +int main(int argc, char **argv) {
219 +
220 + // ------------------------------------------------------------------------
221 + // initialization of netdata plugin
222 +
223 + program_name = "cups.plugin";
224 +
225 + // disable syslog
226 + error_log_syslog = 0;
227 +
228 + // set errors flood protection to 100 logs per hour
229 + error_log_errors_per_period = 100;
230 + error_log_throttle_period = 3600;
231 +
232 + parse_command_line(argc, argv);
233 +
234 + errno = 0;
235 +
236 + dict_dest_job_metrics = dictionary_create(DICTIONARY_FLAG_SINGLE_THREADED);
237 +
238 + // ------------------------------------------------------------------------
239 + // the main loop
240 +
241 + if (debug)
242 + fprintf(stderr, "starting data collection\n");
243 +
244 + time_t started_t = now_monotonic_sec();
245 + size_t iteration = 0;
246 + usec_t step = netdata_update_every * USEC_PER_SEC;
247 +
248 + heartbeat_t hb;
249 + heartbeat_init(&hb);
250 + for (iteration = 0; 1; iteration++)
251 + {
252 + heartbeat_next(&hb, step);
253 +
254 + if (unlikely(netdata_exit))
255 + {
256 + break;
257 + }
258 +
259 + reset_metrics();
260 +
261 + cups_dest_t *dests;
262 + num_dest_total = cupsGetDests2(http, &dests);
263 +
264 + if(unlikely(num_dest_total == 0)) {
265 + // reconnect to cups to check if the server is down.
266 + httpClose(http);
267 + http = httpConnect2(cupsServer(), ippPort(), NULL, AF_UNSPEC, cupsEncryption(), 0, netdata_update_every * 1000, NULL);
268 + if(http == NULL) {
269 + error("cups daemon is not running. Exiting!");
270 + exit(1);
271 + }
272 + }
273 +
274 + cups_dest_t *curr_dest = dests;
275 + int counter = 0;
276 + while (counter < num_dest_total) {
277 + if (counter != 0) {
278 + curr_dest++;
279 + }
280 + counter++;
281 +
282 + const char *printer_uri_supported = cupsGetOption("printer-uri-supported", curr_dest->num_options, curr_dest->options);
283 + if (!printer_uri_supported) {
284 + if(debug)
285 + fprintf(stderr, "destination %s discovered, but not yet setup as a local printer", curr_dest->name);
286 + continue;
287 + }
288 +
289 + const char *printer_is_accepting_jobs = cupsGetOption("printer-is-accepting-jobs", curr_dest->num_options, curr_dest->options);
290 + if (printer_is_accepting_jobs && !strcmp(printer_is_accepting_jobs, "true")) {
291 + num_dest_accepting_jobs++;
292 + }
293 +
294 + const char *printer_is_shared = cupsGetOption("printer-is-shared", curr_dest->num_options, curr_dest->options);
295 + if (printer_is_shared && !strcmp(printer_is_shared, "true")) {
296 + num_dest_shared++;
297 + }
298 +
299 + // TODO use cupsGetIntegerOption
300 + int printer_state = cupsGetIntegerOption("printer-state", curr_dest->num_options, curr_dest->options);
301 + switch (printer_state) {
302 + case 3:
303 + num_dest_idle++;
304 + break;
305 + case 4:
306 + num_dest_printing++;
307 + break;
308 + case 5:
309 + num_dest_stopped++;
310 + break;
311 + case INT_MIN:
312 + if(debug)
313 + fprintf(stderr, "printer state is missing for destination %s", curr_dest->name);
314 + break;
315 + default:
316 + error("Unknown printer state (%d) found.", printer_state);
317 + break;
318 + }
319 +
320 + /*
321 + * flag job metrics to print values.
322 + * This is needed to report also destinations with zero active jobs.
323 + */
324 + struct job_metrics *jm = get_job_metrics(curr_dest->name);
325 + jm->is_collected = 1;
326 + }
327 + cupsFreeDests(num_dest_total, dests);
328 +
329 + if (unlikely(netdata_exit))
330 + break;
331 +
332 + cups_job_t *jobs, *curr_job;
333 + int num_jobs = cupsGetJobs2(http, &jobs, NULL, 0, CUPS_WHICHJOBS_ACTIVE);
334 + int i;
335 + for (i = num_jobs, curr_job = jobs; i > 0; i--, curr_job++) {
336 + struct job_metrics *jm = get_job_metrics(curr_job->dest);
337 + jm->is_collected = 1;
338 +
339 + switch (curr_job->state) {
340 + case IPP_JOB_PENDING:
341 + jm->num_pending++;
342 + jm->size_pending += curr_job->size;
343 + global_job_metrics.num_pending++;
344 + global_job_metrics.size_pending += curr_job->size;
345 + break;
346 + case IPP_JOB_HELD:
347 + jm->num_held++;
348 + jm->size_held += curr_job->size;
349 + global_job_metrics.num_held++;
350 + global_job_metrics.size_held += curr_job->size;
351 + break;
352 + case IPP_JOB_PROCESSING:
353 + jm->num_processing++;
354 + jm->size_processing += curr_job->size;
355 + global_job_metrics.num_processing++;
356 + global_job_metrics.size_processing += curr_job->size;
357 + break;
358 + default:
359 + error("Unsupported job state (%u) found.", curr_job->state);
360 + break;
361 + }
362 + }
363 + cupsFreeJobs(num_jobs, jobs);
364 +
365 + dictionary_get_all_name_value(dict_dest_job_metrics, collect_job_metrics, NULL);
366 +
367 + static int cups_printer_by_option_created = 0;
368 + if (unlikely(!cups_printer_by_option_created))
369 + {
370 + cups_printer_by_option_created = 1;
371 + printf("CHART cups.dest_state '' 'Destinations by state' dests overview dests stacked 100000 %i\n", netdata_update_every);
372 + printf("DIMENSION idle '' absolute 1 1\n");
373 + printf("DIMENSION printing '' absolute 1 1\n");
374 + printf("DIMENSION stopped '' absolute 1 1\n");
375 +
376 + printf("CHART cups.dest_option '' 'Destinations by option' dests overview dests line 100001 %i\n", netdata_update_every);
377 + printf("DIMENSION total '' absolute 1 1\n");
378 + printf("DIMENSION acceptingjobs '' absolute 1 1\n");
379 + printf("DIMENSION shared '' absolute 1 1\n");
380 +
381 + printf("CHART cups.job_num '' 'Total active job number' jobs overview job_num stacked 100002 %i\n", netdata_update_every);
382 + printf("DIMENSION pending '' absolute 1 1\n");
383 + printf("DIMENSION held '' absolute 1 1\n");
384 + printf("DIMENSION processing '' absolute 1 1\n");
385 +
386 + printf("CHART cups.job_size '' 'Total active job size' KB overview job_size stacked 100003 %i\n", netdata_update_every);
387 + printf("DIMENSION pending '' absolute 1 1\n");
388 + printf("DIMENSION held '' absolute 1 1\n");
389 + printf("DIMENSION processing '' absolute 1 1\n");
390 + }
391 +
392 + printf(
393 + "BEGIN cups.dest_state\n"
394 + "SET idle = %d\n"
395 + "SET printing = %d\n"
396 + "SET stopped = %d\n"
397 + "END\n",
398 + num_dest_idle, num_dest_printing, num_dest_stopped);
399 + printf(
400 + "BEGIN cups.dest_option\n"
401 + "SET total = %d\n"
402 + "SET acceptingjobs = %d\n"
403 + "SET shared = %d\n"
404 + "END\n",
405 + num_dest_total, num_dest_accepting_jobs, num_dest_shared);
406 + printf(
407 + "BEGIN cups.job_num\n"
408 + "SET pending = %d\n"
409 + "SET held = %d\n"
410 + "SET processing = %d\n"
411 + "END\n",
412 + global_job_metrics.num_pending, global_job_metrics.num_held, global_job_metrics.num_processing);
413 + printf(
414 + "BEGIN cups.job_size\n"
415 + "SET pending = %d\n"
416 + "SET held = %d\n"
417 + "SET processing = %d\n"
418 + "END\n",
419 + global_job_metrics.size_pending, global_job_metrics.size_held, global_job_metrics.size_processing);
420 +
421 + fflush(stdout);
422 +
423 + if (unlikely(netdata_exit))
424 + break;
425 +
426 + // restart check (14400 seconds)
427 + if (!now_monotonic_sec() - started_t > 14400)
428 + break;
429 + }
430 +
431 + httpClose(http);
432 + info("CUPS process exiting");
433 +}
434 +
435 +#else // !HAVE_CUPS
436 +
437 +int main(int argc, char **argv)
438 +{
439 + fatal("cups.plugin is not compiled.");
440 +}
441 +
442 +#endif // !HAVE_CUPS
collectors/plugins.d/README.md
+1
@@ -9,6 +9,7 @@ plugin|language|O/S|description
9 :---:|:---:|:---:|:---
10 [apps.plugin](../apps.plugin/)|`C`|linux, freebsd|monitors the whole process tree on Linux and FreeBSD and breaks down system resource usage by **process**, **user** and **user group**.
11 [charts.d.plugin](../charts.d.plugin/)|`BASH`|all|a **plugin orchestrator** for data collection modules written in `BASH` v4+.
12 +[cups.plugin](../cups.plugin/)|`C`|all|monitors **CUPS**
13 [fping.plugin](../fping.plugin/)|`C`|all|measures network latency, jitter and packet loss between the monitored node and any number of remote network end points.
14 [freeipmi.plugin](../freeipmi.plugin/)|`C`|linux|collects metrics from enterprise hardware sensors, on Linux servers.
15 [node.d.plugin](../node.d.plugin/)|`node.js`|all|a **plugin orchestrator** for data collection modules written in `node.js`.
configure.ac
+71 -1
@@ -57,6 +57,13 @@ AC_ARG_ENABLE(
57 ,
58 [enable_plugin_freeipmi="detect"]
59 )
60 + AC_ARG_ENABLE(
61 + [plugin-cups],
62 + [AS_HELP_STRING([--enable-plugin-cups], [enable cups plugin @<:@default autodetect@:>@])],
63 + ,
64 + [enable_plugin_cups="detect"]
65 + )
66 +
67 AC_ARG_ENABLE(
68 [pedantic],
69 [AS_HELP_STRING([--enable-pedantic], [enable pedantic compiler warnings @<:@default disabled@:>@])],
@@ -399,6 +406,65 @@ AC_MSG_RESULT([${enable_plugin_freeipmi}])
406 AM_CONDITIONAL([ENABLE_PLUGIN_FREEIPMI], [test "${enable_plugin_freeipmi}" = "yes"])
407
408
409 +# -----------------------------------------------------------------------------
410 +# cups.plugin - libmnl, libnetfilter_acct
411 +
412 + AC_CHECK_LIB([cups], [
413 + cupsEncryption,
414 + cupsFreeDests,
415 + cupsFreeJobs,
416 + cupsGetDests2,
417 + cupsGetIntegerOption,
418 + cupsGetJobs2,
419 + cupsGetOption,
420 + cupsServer,
421 + httpClose,
422 + httpConnect2,
423 + ippPort
424 +],
425 + [AC_CHECK_HEADER(
426 + [cups/cups.h],
427 + [have_cups=yes],
428 + [have_cups=no]
429 + )],
430 + [have_cups=no]
431 +)
432 +
433 +test "${enable_plugin_cups}" = "yes" -a "${have_cups}" != "yes" && \
434 + AC_MSG_ERROR([cups required but not found. Try installing 'cups'])
435 +
436 +AC_ARG_WITH([cups-config],
437 + [AS_HELP_STRING([--with-cups-config=path], [Specify path to cups-config executable.])],
438 + [with_cups_config="$withval"],
439 + [with_cups_config=system]
440 + )
441 +
442 +AS_IF([test "x$with_cups_config" != "xsystem"], [
443 + CUPSCONFIG=$with_cups_config
444 +], [
445 + AC_PATH_TOOL(CUPSCONFIG, [cups-config])
446 + AS_IF([test -z "$CUPSCONFIG"], [
447 + have_cups=no
448 + ])
449 +])
450 +
451 +AC_MSG_CHECKING([if cups.plugin should be enabled])
452 +if test "${enable_plugin_cups}" != "no" -a "${have_cups}" = "yes"; then
453 + enable_plugin_cups="yes"
454 + AC_DEFINE([HAVE_CUPS], [1], [cups usability])
455 +
456 + CUPS_CFLAGS="${CUPS_CFLAGS} `$CUPSCONFIG --cflags`"
457 + CUPS_LIBS="${CUPS_LIBS} `$CUPSCONFIG --image --libs`"
458 +
459 + OPTIONAL_CUPS_CLFAGS="${CUPS_CFLAGS}"
460 + OPTIONAL_CUPS_LIBS="${CUPS_LIBS}"
461 +else
462 + enable_plugin_cups="no"
463 +fi
464 +AC_MSG_RESULT([${enable_plugin_cups}])
465 +AM_CONDITIONAL([ENABLE_PLUGIN_CUPS], [test "${enable_plugin_cups}" = "yes"])
466 +
467 +
468 # -----------------------------------------------------------------------------
469 # nfacct.plugin - libmnl, libnetfilter_acct
470
@@ -463,7 +529,7 @@ if test "${enable_lto}" != "no"; then
529 fi
530 if test "${have_lto}" = "yes"; then
531 oCFLAGS="${CFLAGS}"
466 - CFLAGS="${CFLAGS} -flto ${OPTIONAL_MATH_CLFAGS} ${OPTIONAL_NFACCT_CLFAGS} ${OPTIONAL_ZLIB_CLFAGS} ${OPTIONAL_UUID_CLFAGS} ${OPTIONAL_LIBCAP_CFLAGS} ${OPTIONAL_IPMIMONITORING_CFLAGS}"
532 + CFLAGS="${CFLAGS} -flto ${OPTIONAL_MATH_CLFAGS} ${OPTIONAL_NFACCT_CLFAGS} ${OPTIONAL_ZLIB_CLFAGS} ${OPTIONAL_UUID_CLFAGS} ${OPTIONAL_LIBCAP_CFLAGS} ${OPTIONAL_IPMIMONITORING_CFLAGS} ${OPTIONAL_CUPS_CLFAGS}"
533 ac_cv_c_lto_cross_compile="${enable_lto}"
534 test "${ac_cv_c_lto_cross_compile}" != "yes" && ac_cv_c_lto_cross_compile="no"
535 AC_C_LTO
@@ -534,6 +600,9 @@ AC_SUBST([OPTIONAL_LIBCAP_CFLAGS])
600 AC_SUBST([OPTIONAL_LIBCAP_LIBS])
601 AC_SUBST([OPTIONAL_IPMIMONITORING_CFLAGS])
602 AC_SUBST([OPTIONAL_IPMIMONITORING_LIBS])
603 +AC_SUBST([OPTIONAL_CUPS_CFLAGS])
604 +AC_SUBST([OPTIONAL_CUPS_LIBS])
605 +
606
607 AC_CONFIG_FILES([
608 Makefile
@@ -552,6 +621,7 @@ AC_CONFIG_FILES([
621 collectors/fping.plugin/Makefile
622 collectors/freebsd.plugin/Makefile
623 collectors/freeipmi.plugin/Makefile
624 + collectors/cups.plugin/Makefile
625 collectors/idlejitter.plugin/Makefile
626 collectors/macos.plugin/Makefile
627 collectors/nfacct.plugin/Makefile
docs/Add-more-charts-to-netdata.md
+7
@@ -19,6 +19,7 @@ To collect non-system metrics, netdata supports a plugin architecture. The follo
19 - **[RAID](#raid)**, such as linux software raid (mdadm), MegaRAID
20 - **[Mail Servers](#mail-servers)**, like postfix, exim, dovecot
21 - **[File Servers](#file-servers)**, like samba, NFS, ftp, sftp, WebDAV
22 +- **[Print Servers](#print-servers)**, like CUPS
23 - **[System](#system)**, for processes and other system metrics
24 - **[Sensors](#sensors)**, like temperature, fans speed, voltage, humidity, HDD/SSD S.M.A.R.T attributes
25 - **[Network](#network)**, such as SNMP devices, `fping`, access points, dns_query_time
@@ -55,6 +56,7 @@ To control which plugins netdata run, edit `netdata.conf` and check the `[plugin
56 # proc = yes
57 # diskspace = yes
58 # cgroups = yes
59 + # cups = yes
60 # tc = yes
61 # nfacct = yes
62 # idlejitter = yes
@@ -292,6 +294,11 @@ NFS Client|`C`|This is handled entirely by the netdata daemon.<br/>&nbsp;<br/>Co
294 NFS Server|`C`|This is handled entirely by the netdata daemon.<br/>&nbsp;<br/>Configuration: `netdata.conf`, section `[plugin:proc:/proc/net/rpc/nfsd]`.
295 samba|python<br/>v2 or v3|Performance metrics of Samba SMB2 file sharing.<br/>&nbsp;<br/>documentation page: [python.d.plugin module samba](../collectors/python.d.plugin/samba)<br/>netdata plugin: [python.d.plugin](../collectors/python.d.plugin)<br/>plugin module: [samba.chart.py](../collectors/python.d.plugin/samba)<br/>configuration file: [python.d/samba.conf](../collectors/python.d.plugin/samba)|
296
297 +### Print Servers
298 +
299 +application|language|notes|
300 +:---------:|:------:|:----|
301 +CUPS|C|Charts metrics of printers, jobs and other cups destinations.<br/>&nbsp;<br/>netdata plugin: cups.plugin
302
303 ---
304
docs/generator/buildyaml.sh
+1
@@ -203,6 +203,7 @@ echo -ne " - BASH modules:
203
204 navpart 3 collectors/fping.plugin
205 navpart 3 collectors/freeipmi.plugin
206 +navpart 3 collectors/cups.plugin
207
208 echo -ne " - 'docs/Third-Party-Plugins.md'
209 "
libnetdata/dictionary/dictionary.c
+35
@@ -292,3 +292,38 @@ int dictionary_get_all(DICTIONARY *dict, int (*callback)(void *entry, void *data
292
293 return ret;
294 }
295 +
296 +static int dictionary_walker_name_value(avl *a, int (*callback)(char *name, void *entry, void *data), void *data) {
297 + int total = 0, ret = 0;
298 +
299 + if(a->avl_link[0]) {
300 + ret = dictionary_walker_name_value(a->avl_link[0], callback, data);
301 + if(ret < 0) return ret;
302 + total += ret;
303 + }
304 +
305 + ret = callback(((NAME_VALUE *)a)->name, ((NAME_VALUE *)a)->value, data);
306 + if(ret < 0) return ret;
307 + total += ret;
308 +
309 + if(a->avl_link[1]) {
310 + ret = dictionary_walker_name_value(a->avl_link[1], callback, data);
311 + if (ret < 0) return ret;
312 + total += ret;
313 + }
314 +
315 + return total;
316 +}
317 +
318 +int dictionary_get_all_name_value(DICTIONARY *dict, int (*callback)(char *name, void *entry, void *data), void *data) {
319 + int ret = 0;
320 +
321 + dictionary_read_lock(dict);
322 +
323 + if(likely(dict->values_index.root))
324 + ret = dictionary_walker_name_value(dict->values_index.root, callback, data);
325 +
326 + dictionary_unlock(dict);
327 +
328 + return ret;
329 +}
libnetdata/dictionary/dictionary.h
+1
@@ -44,5 +44,6 @@ extern void *dictionary_get(DICTIONARY *dict, const char *name);
44 extern int dictionary_del(DICTIONARY *dict, const char *name);
45
46 extern int dictionary_get_all(DICTIONARY *dict, int (*callback)(void *entry, void *d), void *data);
47 +extern int dictionary_get_all_name_value(DICTIONARY *dict, int (*callback)(char *name, void *entry, void *d), void *data);
48
49 #endif /* NETDATA_DICTIONARY_H */