2 * Copyright (c) 2013 Holger Weiss <holger@weiss.in-berlin.de>
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.
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"
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" \
52 #define CLIENT_COMMAND_LINE "send_nsca " \
53 "-c `pwd`/" CLIENT_CONF_FILE " " \
54 "-H " LISTEN_ADDRESS " " \
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 " " \
64 #include <sys/types.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 *, ...);
99 main(int argc
, char **argv
)
104 get_options(argc
, argv
);
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
);
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
);
128 run_command(join(CLIENT_COMMAND_LINE
, getenv("NSCA_CLIENT_FLAGS")));
129 cat_fifo(expected_num_lines
);
135 get_options(int argc
, char **argv
)
142 if (strcmp(argv
[1], "--help") == 0) {
146 if (strcmp(argv
[1], "--version") == 0) {
152 while ((option
= getopt(argc
, argv
, "hl:V")) != -1)
158 if ((expected_num_lines
= atol(optarg
)) < 1)
159 die("-l must be a number greater than zero");
169 if (argc
- optind
> 0)
170 die("Unexpected non-option argument: %s", argv
[optind
]);
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
);
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
);
197 run_command(const char *command
)
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. */
210 write_file(const char *file
, const char *contents
)
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
));
225 if (mkfifo(COMMAND_FILE
, 0666) == -1)
226 (void)fprintf(stderr
, "%s: Cannot create %s: %s\n",
227 PROGRAM_NAME
, COMMAND_FILE
, strerror(errno
));
231 cat_fifo(long n_lines
)
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
) {
245 state
= STATE_PRINT_COMMAND
;
246 else if (c
!= '[' && !isdigit(c
) && c
!= ']')
247 die("Got unexpected `%c' from FIFO", c
);
249 if (putchar(c
) == EOF
)
250 die("Cannot write to stdout: %s",
254 state
= STATE_EAT_TIMESTAMP
;
261 die("Cannot read %s: %s", COMMAND_FILE
,
262 got_signal
? "Interrupted" : strerror(errno
));
268 if (unlink(COMMAND_FILE
) == -1)
269 (void)fprintf(stderr
, "%s: Cannot remove %s: %s\n",
270 PROGRAM_NAME
, COMMAND_FILE
, strerror(errno
));
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
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
));
303 print_usage(FILE *stream
)
305 (void)fprintf(stream
,
306 "Usage: %s [<options>]\n\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",
317 (void)system("send_nsca -V");
318 (void)system("nsca-ng -V");
320 (void)puts("The NSCA-ng server uses the POSIX AIO API on this system.");
325 handle_signal(int signal_number
__attribute__((__unused__
)))
331 xatexit(void function(void))
333 if (atexit(function
) != 0)
334 die("Cannot register exit function");
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
,
347 die(const char *format
, ...)
351 (void)fputs(PROGRAM_NAME
": ", stderr
);
353 va_start(ap
, format
);
354 (void)vfprintf(stderr
, format
, ap
);
357 (void)putc('\n', stderr
);
362 /* vim:set joinspaces noexpandtab textwidth=80 cinoptions=(4,u0: */