1 /*
2 * Copyright (c) 2013 Holger Weiss <holger@weiss.in-berlin.de>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 *
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.
14 *
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.
26 */
27
28 #if HAVE_CONFIG_H
29 # include <config.h>
30 #endif
31
32 #include "system.h"
33
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
42
43 #define DEFAULT_CLIENT_CONF "# Created by " PROGRAM_NAME "\n" \
44 "password = \"forty-two\"\n"
45
46 #define DEFAULT_SERVER_CONF "# Created by " PROGRAM_NAME "\n" \
47 "authorize \"*\" {\n" \
48 " password = \"forty-two\"\n" \
49 " commands = \".*\"\n" \
50 "}\n"
51
52 #define CLIENT_COMMAND_LINE "send_nsca " \
53 "-c `pwd`/" CLIENT_CONF_FILE " " \
54 "-H " LISTEN_ADDRESS " " \
55 "-p " LISTEN_PORT
56
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"
63
64 #include <sys/types.h>
65 #include <sys/stat.h>
66 #include <sys/wait.h>
67
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>
77
78 static long expected_num_lines = 1;
79 static volatile char got_signal = 0;
80
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 *, ...);
97
98 int
main(int argc,char ** argv)99 main(int argc, char **argv)
100 {
101 struct sigaction sa;
102 #ifndef __gnu_hurd__
103 int fd;
104 #endif
105
106 get_options(argc, argv);
107
108 sa.sa_flags = 0;
109 sa.sa_handler = handle_signal;
110 (void)sigemptyset(&sa.sa_mask);
111 xsigaction(SIGINT, &sa, NULL);
112 xsigaction(SIGTERM, &sa, NULL);
113 xsigaction(SIGALRM, &sa, NULL);
114 (void)alarm(TIMEOUT);
115
116 if (access(CLIENT_CONF_FILE, F_OK) == -1)
117 write_file(CLIENT_CONF_FILE, DEFAULT_CLIENT_CONF);
118 if (access(SERVER_CONF_FILE, F_OK) == -1)
119 write_file(SERVER_CONF_FILE, DEFAULT_SERVER_CONF);
120
121 create_fifo();
122 xatexit(remove_fifo);
123
124 /*
125 * Make sure there's a FIFO reader when the server starts up, in order
126 * to avoid having to wait ten seconds until the server notices that we
127 * opened the FIFO for reading (in the cat_fifo() function). GNU Hurd
128 * doesn't like this trick, though.
129 */
130 #ifndef __gnu_hurd__
131 if ((fd = open(COMMAND_FILE, O_RDONLY | O_NONBLOCK)) == -1)
132 die("Cannot open %s: %s", COMMAND_FILE, strerror(errno));
133 #endif
134 run_command(join(SERVER_COMMAND_LINE, getenv("NSCA_SERVER_FLAGS")));
135 xatexit(kill_server);
136
137 run_command(join(CLIENT_COMMAND_LINE, getenv("NSCA_CLIENT_FLAGS")));
138 cat_fifo(expected_num_lines);
139 #ifndef __gnu_hurd__
140 (void)close(fd);
141 #endif
142 return EXIT_SUCCESS;
143 }
144
145 static void
get_options(int argc,char ** argv)146 get_options(int argc, char **argv)
147 {
148 extern int optind;
149 extern char *optarg;
150 int option;
151
152 if (argc == 2) {
153 if (strcmp(argv[1], "--help") == 0) {
154 print_usage(stdout);
155 exit(EXIT_SUCCESS);
156 }
157 if (strcmp(argv[1], "--version") == 0) {
158 print_version();
159 exit(EXIT_SUCCESS);
160 }
161 }
162
163 while ((option = getopt(argc, argv, "hl:V")) != -1)
164 switch (option) {
165 case 'h':
166 print_usage(stdout);
167 exit(EXIT_SUCCESS);
168 case 'l':
169 if ((expected_num_lines = atol(optarg)) < 1)
170 die("-l must be a number greater than zero");
171 break;
172 case 'V':
173 print_version();
174 exit(EXIT_SUCCESS);
175 default:
176 print_usage(stderr);
177 exit(EXIT_FAILURE);
178 }
179
180 if (argc - optind > 0)
181 die("Unexpected non-option argument: %s", argv[optind]);
182 }
183
184 static char *
join(const char * part1,const char * part2)185 join(const char *part1, const char *part2)
186 {
187 static char joined[4096];
188 size_t len1 = strlen(part1);
189
190 if (len1 > sizeof(joined))
191 die("Command line too long");
192
193 (void)strcpy(joined, part1);
194
195 if (part2 != NULL) {
196 size_t len2 = strlen(part2);
197
198 if (len1 + 1 + len2 + 1 > sizeof(joined))
199 die("Command line too long");
200
201 (void)strcat(joined, " ");
202 (void)strcat(joined, part2);
203 }
204 return joined;
205 }
206
207 static void
run_command(const char * command)208 run_command(const char *command)
209 {
210 int status;
211
212 if ((status = system(command)) == -1)
213 die("Cannot execute %s: %s", command, strerror(errno));
214 if (WIFEXITED(status) && WEXITSTATUS(status) == 127)
215 exit(77); /* Tell Autotest to skip this test. */
216 if (status != 0)
217 exit(1);
218 }
219
220 static void
write_file(const char * file,const char * contents)221 write_file(const char *file, const char *contents)
222 {
223 FILE *f;
224
225 if ((f = fopen(file, "w")) == NULL)
226 die("Cannot open %s: %s", file, strerror(errno));
227 if (fputs(contents, f) == EOF)
228 die("Cannot write %s: %s", file, strerror(errno));
229 if (fclose(f) == EOF)
230 die("Cannot close %s: %s", file, strerror(errno));
231 }
232
233 static void
create_fifo(void)234 create_fifo(void)
235 {
236 if (mkfifo(COMMAND_FILE, 0666) == -1)
237 (void)fprintf(stderr, "%s: Cannot create %s: %s\n",
238 PROGRAM_NAME, COMMAND_FILE, strerror(errno));
239 }
240
241 static void
cat_fifo(long n_lines)242 cat_fifo(long n_lines)
243 {
244 FILE *fifo;
245 int c;
246 enum {
247 STATE_EAT_TIMESTAMP,
248 STATE_PRINT_COMMAND
249 } state = STATE_EAT_TIMESTAMP;
250
251 do
252 fifo = fopen(COMMAND_FILE, "r");
253 while (fifo == NULL && errno == EINTR); /* Happens on GNU Hurd. */
254 if (fifo == NULL)
255 die("Cannot open %s: %s", COMMAND_FILE, strerror(errno));
256 while ((c = getc(fifo)) != EOF) {
257 if (state == STATE_EAT_TIMESTAMP) {
258 if (c == ' ')
259 state = STATE_PRINT_COMMAND;
260 else if (c != '[' && !isdigit(c) && c != ']')
261 die("Got unexpected `%c' from FIFO", c);
262 } else {
263 if (putchar(c) == EOF)
264 die("Cannot write to stdout: %s",
265 strerror(errno));
266 if (c == '\n') {
267 if (--n_lines > 0)
268 state = STATE_EAT_TIMESTAMP;
269 else
270 break;
271 }
272 }
273 }
274 if (ferror(fifo))
275 die("Cannot read %s: %s", COMMAND_FILE,
276 got_signal ? "Interrupted" : strerror(errno));
277 }
278
279 static void
remove_fifo(void)280 remove_fifo(void)
281 {
282 if (unlink(COMMAND_FILE) == -1)
283 (void)fprintf(stderr, "%s: Cannot remove %s: %s\n",
284 PROGRAM_NAME, COMMAND_FILE, strerror(errno));
285 }
286
287 static void
kill_server(void)288 kill_server(void)
289 {
290 FILE *f;
291 char buf[64];
292 pid_t pid;
293
294 /*
295 * To minimize the risk that the server process interferes with any
296 * following test(s), we KILL the process instead of using the TERM
297 * signal.
298 */
299 if ((f = fopen(SERVER_PID_FILE, "r")) == NULL) {
300 (void)fprintf(stderr, "%s: Cannot open %s: %s\n", PROGRAM_NAME,
301 SERVER_PID_FILE, strerror(errno));
302 return;
303 }
304
305 if (fgets(buf, sizeof(buf), f) == NULL)
306 (void)fprintf(stderr, "%s: Cannot read %s: %s\n", PROGRAM_NAME,
307 SERVER_PID_FILE, ferror(f) ? strerror(errno) : "EOF");
308 else if ((pid = (pid_t)atol(buf)) < 1)
309 (void)fprintf(stderr, "%s: PID file %s contains garbage\n",
310 PROGRAM_NAME, SERVER_PID_FILE);
311 else if (kill(pid, SIGKILL) == -1)
312 (void)fprintf(stderr, "%s: Cannot kill server PID %lu: %s\n",
313 PROGRAM_NAME, (unsigned long)pid, strerror(errno));
314
315 if (fclose(f) == EOF)
316 (void)fprintf(stderr, "%s: Cannot close %s: %s\n", PROGRAM_NAME,
317 SERVER_PID_FILE, strerror(errno));
318 }
319
320 static void
print_usage(FILE * stream)321 print_usage(FILE *stream)
322 {
323 (void)fprintf(stream,
324 "Usage: %s [<options>]\n\n"
325 "Options:\n"
326 " -h Print this usage information and exit.\n"
327 " -l <lines> Read this number of lines from FIFO (default: 1).\n"
328 " -V Print version information and exit.\n",
329 PROGRAM_NAME);
330 }
331
332 static void
print_version(void)333 print_version(void)
334 {
335 (void)system("send_nsca -V");
336 (void)system("nsca-ng -V");
337 #if HAVE_POSIX_AIO
338 (void)puts("The NSCA-ng server uses the POSIX AIO API on this system.");
339 #endif
340 }
341
342 static void
handle_signal(int signal_number)343 handle_signal(int signal_number __attribute__((__unused__)))
344 {
345 got_signal = 1;
346 }
347
348 static void
xatexit(void function (void))349 xatexit(void function(void))
350 {
351 if (atexit(function) != 0)
352 die("Cannot register exit function");
353 }
354
355 static void
xsigaction(int signal_number,const struct sigaction * restrict action,struct sigaction * restrict old_action)356 xsigaction(int signal_number, const struct sigaction * restrict action,
357 struct sigaction * restrict old_action)
358 {
359 if (sigaction(signal_number, action, old_action) == -1)
360 die("Cannot set handler for signal %d: %s", signal_number,
361 strerror(errno));
362 }
363
364 static void
die(const char * format,...)365 die(const char *format, ...)
366 {
367 va_list ap;
368
369 (void)fputs(PROGRAM_NAME ": ", stderr);
370
371 va_start(ap, format);
372 (void)vfprintf(stderr, format, ap);
373 va_end(ap);
374
375 (void)putc('\n', stderr);
376
377 exit(EXIT_FAILURE);
378 }
379
380 /* vim:set joinspaces noexpandtab textwidth=80 cinoptions=(4,u0: */
381