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 * $Id$
18 *****************************************************************************/
20 const char *progname = "check_game";
21 const char *revision = "$Revision$";
22 const char *copyright = "2002-2004";
23 const char *email = "nagiosplug-devel@lists.sourceforge.net";
25 #include "common.h"
26 #include "popen.h"
27 #include "utils.h"
29 int process_arguments (int, char **);
30 int validate_arguments (void);
31 void print_help (void);
32 void print_usage (void);
34 #define QSTAT_DATA_DELIMITER ","
36 #define QSTAT_HOST_ERROR "ERROR"
37 #define QSTAT_HOST_DOWN "DOWN"
38 #define QSTAT_HOST_TIMEOUT "TIMEOUT"
39 #define QSTAT_MAX_RETURN_ARGS 12
41 char *server_ip;
42 char *game_type;
43 int port = 0;
45 int verbose;
47 int qstat_game_players_max = -1;
48 int qstat_game_players = -1;
49 int qstat_game_field = -1;
50 int qstat_map_field = -1;
51 int qstat_ping_field = -1;
54 int
55 main (int argc, char **argv)
56 {
57 char *command_line;
58 int result = STATE_UNKNOWN;
59 FILE *fp;
60 char input_buffer[MAX_INPUT_BUFFER];
61 char *p, *ret[QSTAT_MAX_RETURN_ARGS];
62 int i;
64 setlocale (LC_ALL, "");
65 bindtextdomain (PACKAGE, LOCALEDIR);
66 textdomain (PACKAGE);
68 if (process_arguments (argc, argv) == ERROR)
69 usage4 (_("Could not parse arguments"));
71 result = STATE_OK;
73 /* create the command line to execute */
74 asprintf (&command_line, "%s -raw %s -%s %s",
75 PATH_TO_QSTAT, QSTAT_DATA_DELIMITER, game_type, server_ip);
77 if (port)
78 asprintf (&command_line, "%s:%-d", command_line, port);
80 if (verbose > 0)
81 printf ("%s\n", command_line);
83 /* run the command */
84 fp = spopen (command_line);
85 if (fp == NULL) {
86 printf (_("Could not open pipe: %s\n"), command_line);
87 return STATE_UNKNOWN;
88 }
90 fgets (input_buffer, MAX_INPUT_BUFFER - 1, fp); /* Only interested in the first line */
92 /* strip the newline character from the end of the input */
93 input_buffer[strlen (input_buffer) - 1] = 0;
95 /* sanity check */
96 /* was thinking about running qstat without any options, capturing the
97 -default line, parsing it & making an array of all know server types
98 but thought this would be too much hassle considering this is a tool
99 for intelligent sysadmins (ha). Could put a static array of known
100 server types in a header file but then we'd be limiting ourselves
102 In the end, I figured I'd simply let an error occur & then trap it
103 */
105 if (!strncmp (input_buffer, "unknown option", 14)) {
106 printf (_("CRITICAL - Host type parameter incorrect!\n"));
107 result = STATE_CRITICAL;
108 return result;
109 }
111 /* initialize the returned data buffer */
112 for (i = 0; i < QSTAT_MAX_RETURN_ARGS; i++)
113 ret[i] = strdup("");
115 i = 0;
116 p = (char *) strtok (input_buffer, QSTAT_DATA_DELIMITER);
117 while (p != NULL) {
118 ret[i] = p;
119 p = (char *) strtok (NULL, QSTAT_DATA_DELIMITER);
120 i++;
121 if (i >= QSTAT_MAX_RETURN_ARGS)
122 break;
123 }
125 if (strstr (ret[2], QSTAT_HOST_ERROR)) {
126 printf (_("CRITICAL - Host not found\n"));
127 result = STATE_CRITICAL;
128 }
129 else if (strstr (ret[2], QSTAT_HOST_DOWN)) {
130 printf (_("CRITICAL - Game server down or unavailable\n"));
131 result = STATE_CRITICAL;
132 }
133 else if (strstr (ret[2], QSTAT_HOST_TIMEOUT)) {
134 printf (_("CRITICAL - Game server timeout\n"));
135 result = STATE_CRITICAL;
136 }
137 else {
138 printf ("OK: %s/%s %s (%s), Ping: %s ms|%s %s\n",
139 ret[qstat_game_players],
140 ret[qstat_game_players_max],
141 ret[qstat_game_field],
142 ret[qstat_map_field],
143 ret[qstat_ping_field],
144 perfdata ("players", atol(ret[qstat_game_players]), "",
145 FALSE, 0, FALSE, 0,
146 TRUE, 0, TRUE, atol(ret[qstat_game_players_max])),
147 fperfdata ("ping", strtod(ret[qstat_ping_field], NULL), "",
148 FALSE, 0, FALSE, 0,
149 TRUE, 0, FALSE, 0));
150 }
152 /* close the pipe */
153 spclose (fp);
155 return result;
156 }
159 int
160 process_arguments (int argc, char **argv)
161 {
162 int c;
164 int opt_index = 0;
165 static struct option long_opts[] = {
166 {"help", no_argument, 0, 'h'},
167 {"version", no_argument, 0, 'V'},
168 {"verbose", no_argument, 0, 'v'},
169 {"timeout", required_argument, 0, 't'},
170 {"hostname", required_argument, 0, 'H'},
171 {"port", required_argument, 0, 'P'},
172 {"game-type", required_argument, 0, 'G'},
173 {"map-field", required_argument, 0, 'm'},
174 {"ping-field", required_argument, 0, 'p'},
175 {"game-field", required_argument, 0, 'g'},
176 {"players-field", required_argument, 0, 129},
177 {"max-players-field", required_argument, 0, 130},
178 {0, 0, 0, 0}
179 };
181 if (argc < 2)
182 return ERROR;
184 for (c = 1; c < argc; c++) {
185 if (strcmp ("-mf", argv[c]) == 0)
186 strcpy (argv[c], "-m");
187 else if (strcmp ("-pf", argv[c]) == 0)
188 strcpy (argv[c], "-p");
189 else if (strcmp ("-gf", argv[c]) == 0)
190 strcpy (argv[c], "-g");
191 }
193 while (1) {
194 c = getopt_long (argc, argv, "hVvt:H:P:G:g:p:m:", long_opts, &opt_index);
196 if (c == -1 || c == EOF)
197 break;
199 switch (c) {
200 case '?': /* args not parsable */
201 usage2 (_("Unknown argument"), optarg);
202 case 'h': /* help */
203 print_help ();
204 exit (STATE_OK);
205 case 'V': /* version */
206 print_revision (progname, revision);
207 exit (STATE_OK);
208 case 'v': /* version */
209 verbose = TRUE;
210 break;
211 case 't': /* timeout period */
212 timeout_interval = atoi (optarg);
213 break;
214 case 'H': /* hostname */
215 if (strlen (optarg) >= MAX_HOST_ADDRESS_LENGTH)
216 die (STATE_UNKNOWN, _("Input buffer overflow\n"));
217 server_ip = optarg;
218 break;
219 case 'P': /* port */
220 port = atoi (optarg);
221 break;
222 case 'G': /* hostname */
223 if (strlen (optarg) >= MAX_INPUT_BUFFER)
224 die (STATE_UNKNOWN, _("Input buffer overflow\n"));
225 game_type = optarg;
226 break;
227 case 'p': /* index of ping field */
228 qstat_ping_field = atoi (optarg);
229 if (qstat_ping_field < 0 || qstat_ping_field > QSTAT_MAX_RETURN_ARGS)
230 return ERROR;
231 break;
232 case 'm': /* index on map field */
233 qstat_map_field = atoi (optarg);
234 if (qstat_map_field < 0 || qstat_map_field > QSTAT_MAX_RETURN_ARGS)
235 return ERROR;
236 break;
237 case 'g': /* index of game field */
238 qstat_game_field = atoi (optarg);
239 if (qstat_game_field < 0 || qstat_game_field > QSTAT_MAX_RETURN_ARGS)
240 return ERROR;
241 break;
242 case 129: /* index of player count field */
243 qstat_game_players = atoi (optarg);
244 if (qstat_game_players_max == 0)
245 qstat_game_players_max = qstat_game_players - 1;
246 if (qstat_game_players < 0 || qstat_game_players > QSTAT_MAX_RETURN_ARGS)
247 return ERROR;
248 break;
249 case 130: /* index of max players field */
250 qstat_game_players_max = atoi (optarg);
251 if (qstat_game_players_max < 0 || qstat_game_players_max > QSTAT_MAX_RETURN_ARGS)
252 return ERROR;
253 break;
254 }
255 }
257 c = optind;
258 /* first option is the game type */
259 if (!game_type && c<argc)
260 game_type = strdup (argv[c++]);
262 /* Second option is the server name */
263 if (!server_ip && c<argc)
264 server_ip = strdup (argv[c++]);
266 return validate_arguments ();
267 }
270 int
271 validate_arguments (void)
272 {
273 if (qstat_game_players_max < 0)
274 qstat_game_players_max = 4;
276 if (qstat_game_players < 0)
277 qstat_game_players = 5;
279 if (qstat_game_field < 0)
280 qstat_game_field = 2;
282 if (qstat_map_field < 0)
283 qstat_map_field = 3;
285 if (qstat_ping_field < 0)
286 qstat_ping_field = 5;
288 return OK;
289 }
292 void
293 print_help (void)
294 {
295 print_revision (progname, revision);
297 printf ("Copyright (c) 1999 Ian Cass, Knowledge Matters Limited\n");
298 printf (COPYRIGHT, copyright, email);
300 printf (_("This plugin tests game server connections with the specified host."));
302 print_usage ();
304 printf (_(UT_HELP_VRSN));
306 printf (_("\
307 <game> = Game type that is recognised by qstat (without the leading dash)\n\
308 <ip_address> = The IP address of the device you wish to query\n\
309 [port] = Optional port of which to connect\n\
310 [game_field] = Field number in raw qstat output that contains game name\n\
311 [map_field] = Field number in raw qstat output that contains map name\n\
312 [ping_field] = Field number in raw qstat output that contains ping time\n"));
314 printf (_(UT_TIMEOUT), DEFAULT_SOCKET_TIMEOUT);
316 printf (_("\n\
317 Notes:\n\
318 - This plugin uses the 'qstat' command, the popular game server status query tool .\n\
319 If you don't have the package installed, you will need to download it from\n\
320 http://www.activesw.com/people/steve/qstat.html before you can use this plugin.\n"));
322 printf (_(UT_SUPPORT));
323 }
327 void
328 print_usage (void)
329 {
330 printf ("\
331 Usage: %s <game> <ip_address> [-p port] [-gf game_field] [-mf map_field]\n\
332 [-pf ping_field]\n", progname);
333 }
335 /******************************************************************************
336 *
337 * Test Cases:
338 *
339 * ./check_game --players 7 -p 8 --map 5 qs 67.20.190.61 26000
340 *
341 * qstat -raw , -qs 67.20.190.61
342 * ==> QS,67.20.190.61,Nightmare.fintek.ca,67.20.190.61:26000,3,e2m1,6,0,83,0
343 *
344 * qstat -qs 67.20.190.61
345 * ==> ADDRESS PLAYERS MAP RESPONSE TIME NAME
346 * ==> 67.20.190.61 0/ 6 e2m1 79 / 0 Nightmare.fintek.ca
347 *
348 ******************************************************************************/