1 /******************************************************************************
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, write to the Free Software
15 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
16 *
17 *****************************************************************************/
19 const char *progname = "check_game";
20 const char *revision = "$Revision$";
21 const char *copyright = "2002-2003";
22 const char *email = "nagiosplug-devel@lists.sourceforge.net";
24 #include "common.h"
25 #include "popen.h"
26 #include "utils.h"
28 int process_arguments (int, char **);
29 int validate_arguments (void);
30 void print_help (void);
31 void print_usage (void);
33 #define QSTAT_DATA_DELIMITER ","
35 #define QSTAT_HOST_ERROR "ERROR"
36 #define QSTAT_HOST_DOWN "DOWN"
37 #define QSTAT_HOST_TIMEOUT "TIMEOUT"
38 #define QSTAT_MAX_RETURN_ARGS 12
40 char *server_ip;
41 char *game_type;
42 int port = 0;
44 int verbose;
46 int qstat_game_players_max = -1;
47 int qstat_game_players = -1;
48 int qstat_game_field = -1;
49 int qstat_map_field = -1;
50 int qstat_ping_field = -1;
53 int
54 main (int argc, char **argv)
55 {
56 char *command_line;
57 int result;
58 FILE *fp;
59 char input_buffer[MAX_INPUT_BUFFER];
60 char *p, *ret[QSTAT_MAX_RETURN_ARGS];
61 int i;
63 setlocale (LC_ALL, "");
64 bindtextdomain (PACKAGE, LOCALEDIR);
65 textdomain (PACKAGE);
67 result = process_arguments (argc, argv);
69 if (result != OK) {
70 printf (_("Incorrect arguments supplied\n"));
71 printf ("\n");
72 print_revision (progname, revision);
73 printf (_("Copyright (c) 1999 Ian Cass, Knowledge Matters Limited\n"));
74 printf (_("License: GPL\n"));
75 printf ("\n");
76 return STATE_UNKNOWN;
77 }
79 result = STATE_OK;
81 /* create the command line to execute */
82 asprintf (&command_line, "%s -raw %s -%s %s",
83 PATH_TO_QSTAT, QSTAT_DATA_DELIMITER, game_type, server_ip);
85 if (port)
86 asprintf (&command_line, "%s:%-d", command_line, port);
88 if (verbose > 0)
89 printf ("%s\n", command_line);
91 /* run the command */
92 fp = spopen (command_line);
93 if (fp == NULL) {
94 printf (_("Error - Could not open pipe: %s\n"), command_line);
95 return STATE_UNKNOWN;
96 }
98 fgets (input_buffer, MAX_INPUT_BUFFER - 1, fp); /* Only interested in the first line */
100 /* strip the newline character from the end of the input */
101 input_buffer[strlen (input_buffer) - 1] = 0;
103 /* sanity check */
104 /* was thinking about running qstat without any options, capturing the
105 -default line, parsing it & making an array of all know server types
106 but thought this would be too much hassle considering this is a tool
107 for intelligent sysadmins (ha). Could put a static array of known
108 server types in a header file but then we'd be limiting ourselves
110 In the end, I figured I'd simply let an error occur & then trap it
111 */
113 if (!strncmp (input_buffer, "unknown option", 14)) {
114 printf (_("ERROR: Host type parameter incorrect!\n"));
115 result = STATE_CRITICAL;
116 return result;
117 }
119 /* initialize the returned data buffer */
120 for (i = 0; i < QSTAT_MAX_RETURN_ARGS; i++)
121 ret[i] = strdup("");
123 i = 0;
124 p = (char *) strtok (input_buffer, QSTAT_DATA_DELIMITER);
125 while (p != NULL) {
126 ret[i] = p;
127 p = (char *) strtok (NULL, QSTAT_DATA_DELIMITER);
128 i++;
129 if (i >= QSTAT_MAX_RETURN_ARGS)
130 break;
131 }
133 if (strstr (ret[2], QSTAT_HOST_ERROR)) {
134 printf ("ERROR: Host not found\n");
135 result = STATE_CRITICAL;
136 }
137 else if (strstr (ret[2], QSTAT_HOST_DOWN)) {
138 printf ("ERROR: Game server down or unavailable\n");
139 result = STATE_CRITICAL;
140 }
141 else if (strstr (ret[2], QSTAT_HOST_TIMEOUT)) {
142 printf ("ERROR: Game server timeout\n");
143 result = STATE_CRITICAL;
144 }
145 else {
146 printf ("OK: %s/%s %s (%s), Ping: %s ms|%s %s\n",
147 ret[qstat_game_players],
148 ret[qstat_game_players_max],
149 ret[qstat_game_field],
150 ret[qstat_map_field],
151 ret[qstat_ping_field],
152 perfdata ("players", atol(ret[qstat_game_players]), "",
153 FALSE, 0, FALSE, 0,
154 TRUE, 0, TRUE, atol(ret[qstat_game_players_max])),
155 perfdata ("ping", atol(ret[qstat_ping_field]), "",
156 FALSE, 0, FALSE, 0,
157 TRUE, 0, FALSE, 0));
158 }
160 /* close the pipe */
161 spclose (fp);
163 return result;
164 }
168 int
169 process_arguments (int argc, char **argv)
170 {
171 int c;
173 int opt_index = 0;
174 static struct option long_opts[] = {
175 {"help", no_argument, 0, 'h'},
176 {"version", no_argument, 0, 'V'},
177 {"verbose", no_argument, 0, 'v'},
178 {"timeout", required_argument, 0, 't'},
179 {"hostname", required_argument, 0, 'H'},
180 {"port", required_argument, 0, 'P'},
181 {"game-type", required_argument, 0, 'G'},
182 {"map-field", required_argument, 0, 'm'},
183 {"ping-field", required_argument, 0, 'p'},
184 {"game-field", required_argument, 0, 'g'},
185 {"players-field", required_argument, 0, 129},
186 {"max-players-field", required_argument, 0, 130},
187 {0, 0, 0, 0}
188 };
190 if (argc < 2)
191 return ERROR;
193 for (c = 1; c < argc; c++) {
194 if (strcmp ("-mf", argv[c]) == 0)
195 strcpy (argv[c], "-m");
196 else if (strcmp ("-pf", argv[c]) == 0)
197 strcpy (argv[c], "-p");
198 else if (strcmp ("-gf", argv[c]) == 0)
199 strcpy (argv[c], "-g");
200 }
202 while (1) {
203 c = getopt_long (argc, argv, "hVvt:H:P:G:g:p:m:", long_opts, &opt_index);
205 if (c == -1 || c == EOF)
206 break;
208 switch (c) {
209 case '?': /* args not parsable */
210 printf (_("%s: Unknown argument: %s\n\n"), progname, optarg);
211 print_usage ();
212 exit (STATE_UNKNOWN);
213 case 'h': /* help */
214 print_help ();
215 exit (STATE_OK);
216 case 'V': /* version */
217 print_revision (progname, revision);
218 exit (STATE_OK);
219 case 'v': /* version */
220 verbose = TRUE;
221 break;
222 case 't': /* timeout period */
223 timeout_interval = atoi (optarg);
224 break;
225 case 'H': /* hostname */
226 if (strlen (optarg) >= MAX_HOST_ADDRESS_LENGTH)
227 die (STATE_UNKNOWN, _("Input buffer overflow\n"));
228 server_ip = optarg;
229 break;
230 case 'P': /* port */
231 port = atoi (optarg);
232 break;
233 case 'G': /* hostname */
234 if (strlen (optarg) >= MAX_INPUT_BUFFER)
235 die (STATE_UNKNOWN, _("Input buffer overflow\n"));
236 game_type = optarg;
237 break;
238 case 'p': /* index of ping field */
239 qstat_ping_field = atoi (optarg);
240 if (qstat_ping_field < 0 || qstat_ping_field > QSTAT_MAX_RETURN_ARGS)
241 return ERROR;
242 break;
243 case 'm': /* index on map field */
244 qstat_map_field = atoi (optarg);
245 if (qstat_map_field < 0 || qstat_map_field > QSTAT_MAX_RETURN_ARGS)
246 return ERROR;
247 break;
248 case 'g': /* index of game field */
249 qstat_game_field = atoi (optarg);
250 if (qstat_game_field < 0 || qstat_game_field > QSTAT_MAX_RETURN_ARGS)
251 return ERROR;
252 break;
253 case 129: /* index of player count field */
254 qstat_game_players = atoi (optarg);
255 if (qstat_game_players_max == 0)
256 qstat_game_players_max = qstat_game_players - 1;
257 if (qstat_game_players < 0 || qstat_game_players > QSTAT_MAX_RETURN_ARGS)
258 return ERROR;
259 break;
260 case 130: /* index of max players field */
261 qstat_game_players_max = atoi (optarg);
262 if (qstat_game_players_max < 0 || qstat_game_players_max > QSTAT_MAX_RETURN_ARGS)
263 return ERROR;
264 break;
265 }
266 }
268 c = optind;
269 /* first option is the game type */
270 if (!game_type && c<argc)
271 game_type = strdup (argv[c++]);
273 /* Second option is the server name */
274 if (!server_ip && c<argc)
275 server_ip = strdup (argv[c++]);
277 return validate_arguments ();
278 }
280 int
281 validate_arguments (void)
282 {
283 if (qstat_game_players_max < 0)
284 qstat_game_players_max = 4;
286 if (qstat_game_players < 0)
287 qstat_game_players = 5;
289 if (qstat_game_field < 0)
290 qstat_game_field = 2;
292 if (qstat_map_field < 0)
293 qstat_map_field = 3;
295 if (qstat_ping_field < 0)
296 qstat_ping_field = 5;
298 return OK;
299 }
305 \f
306 void
307 print_help (void)
308 {
309 print_revision (progname, revision);
311 printf (_(COPYRIGHT), copyright, email);
313 printf (_("This plugin tests %s connections with the specified host."), progname);
315 print_usage ();
317 printf (_(UT_HELP_VRSN));
319 printf (_("\
320 <game> = Game type that is recognised by qstat (without the leading dash)\n\
321 <ip_address> = The IP address of the device you wish to query\n\
322 [port] = Optional port of which to connect\n\
323 [game_field] = Field number in raw qstat output that contains game name\n\
324 [map_field] = Field number in raw qstat output that contains map name\n\
325 [ping_field] = Field number in raw qstat output that contains ping time\n"));
327 printf (_(UT_TIMEOUT), DEFAULT_SOCKET_TIMEOUT);
329 printf (_("\n\
330 Notes:\n\
331 - This plugin uses the 'qstat' command, the popular game server status query tool .\n\
332 If you don't have the package installed, you will need to download it from\n\
333 http://www.activesw.com/people/steve/qstat.html before you can use this plugin.\n"));
335 printf (_(UT_SUPPORT));
336 }
341 void
342 print_usage (void)
343 {
344 printf (_("\
345 Usage: %s <game> <ip_address> [-p port] [-gf game_field] [-mf map_field]\n\
346 [-pf ping_field]\n"), progname);
347 printf (_(UT_HLP_VRS), progname, progname);
348 }
350 /******************************************************************************
351 *
352 * Test Cases:
353 *
354 * ./check_game --players 7 -p 8 --map 5 qs 67.20.190.61 26000
355 *
356 * qstat -raw , -qs 67.20.190.61
357 * ==> QS,67.20.190.61,Nightmare.fintek.ca,67.20.190.61:26000,3,e2m1,6,0,83,0
358 *
359 * qstat -qs 67.20.190.61
360 * ==> ADDRESS PLAYERS MAP RESPONSE TIME NAME
361 * ==> 67.20.190.61 0/ 6 e2m1 79 / 0 Nightmare.fintek.ca
362 *
363 ******************************************************************************/