Use ".cfg" as the configuration file extension
[nsca-ng.git] / tests / test_nsca.c
bloba4b34221c239e2a6a60670d7c716f1d2898f48e9
1 /*
2 * Copyright (c) 2013 Holger Weiss <holger@weiss.in-berlin.de>
3 * All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
8 * 1. Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation
13 * and/or other materials provided with the distribution.
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
19 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
20 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
21 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
24 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25 * POSSIBILITY OF SUCH DAMAGE.
28 #if HAVE_CONFIG_H
29 # include <config.h>
30 #endif
32 #include "system.h"
34 #define PROGRAM_NAME "test_nsca"
35 #define LISTEN_ADDRESS "127.0.0.1"
36 #define LISTEN_PORT "12345" /* Don't interfere with a poduction server. */
37 #define COMMAND_FILE "fifo"
38 #define SERVER_PID_FILE "server.pid"
39 #define CLIENT_CONF_FILE "client.cfg"
40 #define SERVER_CONF_FILE "server.cfg"
41 #define TIMEOUT 10
43 #define DEFAULT_CLIENT_CONF "# Created by " PROGRAM_NAME "\n" \
44 "password = \"forty-two\"\n"
46 #define DEFAULT_SERVER_CONF "# Created by " PROGRAM_NAME "\n" \
47 "authorize \"*\" {\n" \
48 " password = \"forty-two\"\n" \
49 " commands = \".*\"\n" \
50 "}\n"
52 #define CLIENT_COMMAND_LINE "send_nsca " \
53 "-c `pwd`/" CLIENT_CONF_FILE " " \
54 "-H " LISTEN_ADDRESS " " \
55 "-p " LISTEN_PORT
57 #define SERVER_COMMAND_LINE "nsca-ng " \
58 "-c `pwd`/" SERVER_CONF_FILE " " \
59 "-C `pwd`/" COMMAND_FILE " " \
60 "-P `pwd`/" SERVER_PID_FILE " " \
61 "-b " LISTEN_ADDRESS ":" LISTEN_PORT " " \
62 "-l 0"
64 #include <sys/types.h>
65 #include <sys/stat.h>
66 #include <sys/wait.h>
68 #include <ctype.h>
69 #include <errno.h>
70 #include <fcntl.h>
71 #include <signal.h>
72 #include <stdarg.h>
73 #include <stdio.h>
74 #include <stdlib.h>
75 #include <string.h>
76 #include <unistd.h>
78 static long expected_num_lines = 1;
79 static volatile char got_signal = 0;
81 static void get_options(int, char **);
82 static char *join(const char *, const char *);
83 static void run_command(const char *);
84 static void write_file(const char *, const char *);
85 static void create_fifo(void);
86 static void cat_fifo(long);
87 static void remove_fifo(void);
88 static void kill_server(void);
89 static void print_usage(FILE *);
90 static void print_version(void);
91 static void handle_signal(int);
92 static void xatexit(void (*)(void));
93 static void xsigaction(int, const struct sigaction * restrict,
94 struct sigaction * restrict);
95 static void __attribute__((__format__(__printf__, 1, 2), __noreturn__))
96 die(const char *, ...);
98 int
99 main(int argc, char **argv)
101 struct sigaction sa;
102 int fd;
104 get_options(argc, argv);
106 sa.sa_flags = 0;
107 sa.sa_handler = handle_signal;
108 (void)sigemptyset(&sa.sa_mask);
109 xsigaction(SIGINT, &sa, NULL);
110 xsigaction(SIGTERM, &sa, NULL);
111 xsigaction(SIGALRM, &sa, NULL);
112 (void)alarm(TIMEOUT);
114 if (access(CLIENT_CONF_FILE, F_OK) == -1)
115 write_file(CLIENT_CONF_FILE, DEFAULT_CLIENT_CONF);
116 if (access(SERVER_CONF_FILE, F_OK) == -1)
117 write_file(SERVER_CONF_FILE, DEFAULT_SERVER_CONF);
119 create_fifo();
120 xatexit(remove_fifo);
122 /* Make sure there's a FIFO reader when the server starts up. */
123 fd = open(COMMAND_FILE, O_RDONLY | O_NONBLOCK);
124 run_command(join(SERVER_COMMAND_LINE, getenv("NSCA_SERVER_FLAGS")));
125 xatexit(kill_server);
126 (void)close(fd);
128 run_command(join(CLIENT_COMMAND_LINE, getenv("NSCA_CLIENT_FLAGS")));
129 cat_fifo(expected_num_lines);
131 return EXIT_SUCCESS;
134 static void
135 get_options(int argc, char **argv)
137 extern int optind;
138 extern char *optarg;
139 int option;
141 if (argc == 2) {
142 if (strcmp(argv[1], "--help") == 0) {
143 print_usage(stdout);
144 exit(EXIT_SUCCESS);
146 if (strcmp(argv[1], "--version") == 0) {
147 print_version();
148 exit(EXIT_SUCCESS);
152 while ((option = getopt(argc, argv, "hl:V")) != -1)
153 switch (option) {
154 case 'h':
155 print_usage(stdout);
156 exit(EXIT_SUCCESS);
157 case 'l':
158 if ((expected_num_lines = atol(optarg)) < 1)
159 die("-l must be a number greater than zero");
160 break;
161 case 'V':
162 print_version();
163 exit(EXIT_SUCCESS);
164 default:
165 print_usage(stderr);
166 exit(EXIT_FAILURE);
169 if (argc - optind > 0)
170 die("Unexpected non-option argument: %s", argv[optind]);
173 static char *
174 join(const char *part1, const char *part2)
176 static char joined[4096];
177 size_t len1 = strlen(part1);
179 if (len1 > sizeof(joined))
180 die("Command line too long");
182 (void)strcpy(joined, part1);
184 if (part2 != NULL) {
185 size_t len2 = strlen(part2);
187 if (len1 + 1 + len2 + 1 > sizeof(joined))
188 die("Command line too long");
190 (void)strcat(joined, " ");
191 (void)strcat(joined, part2);
193 return joined;
196 static void
197 run_command(const char *command)
199 int status;
201 if ((status = system(command)) == -1)
202 die("Cannot execute %s: %s", command, strerror(errno));
203 if (WIFEXITED(status) && WEXITSTATUS(status) == 127)
204 exit(77); /* Tell Autotest to skip this test. */
205 if (status != 0)
206 exit(1);
209 static void
210 write_file(const char *file, const char *contents)
212 FILE *f;
214 if ((f = fopen(file, "w")) == NULL)
215 die("Cannot open %s: %s", file, strerror(errno));
216 if (fputs(contents, f) == EOF)
217 die("Cannot write %s: %s", file, strerror(errno));
218 if (fclose(f) == EOF)
219 die("Cannot close %s: %s", file, strerror(errno));
222 static void
223 create_fifo(void)
225 if (mkfifo(COMMAND_FILE, 0666) == -1)
226 (void)fprintf(stderr, "%s: Cannot create %s: %s\n",
227 PROGRAM_NAME, COMMAND_FILE, strerror(errno));
230 static void
231 cat_fifo(long n_lines)
233 FILE *fifo;
234 int c;
235 enum {
236 STATE_EAT_TIMESTAMP,
237 STATE_PRINT_COMMAND
238 } state = STATE_EAT_TIMESTAMP;
240 if ((fifo = fopen(COMMAND_FILE, "r")) == NULL)
241 die("Cannot open %s: %s", COMMAND_FILE, strerror(errno));
242 while ((c = getc(fifo)) != EOF) {
243 if (state == STATE_EAT_TIMESTAMP) {
244 if (c == ' ')
245 state = STATE_PRINT_COMMAND;
246 else if (c != '[' && !isdigit(c) && c != ']')
247 die("Got unexpected `%c' from FIFO", c);
248 } else {
249 if (putchar(c) == EOF)
250 die("Cannot write to stdout: %s",
251 strerror(errno));
252 if (c == '\n') {
253 if (--n_lines > 0)
254 state = STATE_EAT_TIMESTAMP;
255 else
256 break;
260 if (ferror(fifo))
261 die("Cannot read %s: %s", COMMAND_FILE,
262 got_signal ? "Interrupted" : strerror(errno));
265 static void
266 remove_fifo(void)
268 if (unlink(COMMAND_FILE) == -1)
269 (void)fprintf(stderr, "%s: Cannot remove %s: %s\n",
270 PROGRAM_NAME, COMMAND_FILE, strerror(errno));
273 static void
274 kill_server(void)
276 FILE *f;
277 char buf[64];
278 pid_t pid;
281 * To minimize the risk that the server process interferes with any
282 * following test(s), we KILL the process instead of using the TERM
283 * signal.
285 if ((f = fopen(SERVER_PID_FILE, "r")) == NULL)
286 (void)fprintf(stderr, "%s: Cannot open %s: %s\n", PROGRAM_NAME,
287 SERVER_PID_FILE, strerror(errno));
288 else if (fgets(buf, sizeof(buf), f) == NULL)
289 (void)fprintf(stderr, "%s: Cannot read %s: %s\n", PROGRAM_NAME,
290 SERVER_PID_FILE, ferror(f) ? strerror(errno) : "EOF");
291 else if ((pid = (pid_t)atol(buf)) < 1)
292 (void)fprintf(stderr, "%s: PID file %s contains garbage\n",
293 PROGRAM_NAME, SERVER_PID_FILE);
294 else if (kill(pid, SIGKILL) == -1)
295 (void)fprintf(stderr, "%s: Cannot kill server PID %lu: %s\n",
296 PROGRAM_NAME, (unsigned long)pid, strerror(errno));
297 else if (fclose(f) == EOF)
298 (void)fprintf(stderr, "%s: Cannot close %s: %s\n", PROGRAM_NAME,
299 SERVER_PID_FILE, strerror(errno));
302 static void
303 print_usage(FILE *stream)
305 (void)fprintf(stream,
306 "Usage: %s [<options>]\n\n"
307 "Options:\n"
308 " -h Print this usage information and exit.\n"
309 " -l <lines> Read this number of lines from FIFO (default: 1).\n"
310 " -V Print version information and exit.\n",
311 PROGRAM_NAME);
314 static void
315 print_version(void)
317 (void)system("send_nsca -V");
318 (void)system("nsca-ng -V");
319 #if HAVE_POSIX_AIO
320 (void)puts("The NSCA-ng server uses the POSIX AIO API on this system.");
321 #endif
324 static void
325 handle_signal(int signal_number __attribute__((__unused__)))
327 got_signal = 1;
330 static void
331 xatexit(void function(void))
333 if (atexit(function) != 0)
334 die("Cannot register exit function");
337 static void
338 xsigaction(int signal_number, const struct sigaction * restrict action,
339 struct sigaction * restrict old_action)
341 if (sigaction(signal_number, action, old_action) == -1)
342 die("Cannot set handler for signal %d: %s", signal_number,
343 strerror(errno));
346 static void
347 die(const char *format, ...)
349 va_list ap;
351 (void)fputs(PROGRAM_NAME ": ", stderr);
353 va_start(ap, format);
354 (void)vfprintf(stderr, format, ap);
355 va_end(ap);
357 (void)putc('\n', stderr);
359 exit(EXIT_FAILURE);
362 /* vim:set joinspaces noexpandtab textwidth=80 cinoptions=(4,u0: */