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 "utils.h"
27 #include "runcmd.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 char *p, *ret[QSTAT_MAX_RETURN_ARGS];
60 size_t i = 0;
61 output chld_out;
63 setlocale (LC_ALL, "");
64 bindtextdomain (PACKAGE, LOCALEDIR);
65 textdomain (PACKAGE);
67 if (process_arguments (argc, argv) == ERROR)
68 usage_va(_("Could not parse arguments"));
70 result = STATE_OK;
72 /* create the command line to execute */
73 asprintf (&command_line, "%s -raw %s -%s %s",
74 PATH_TO_QSTAT, QSTAT_DATA_DELIMITER, game_type, server_ip);
76 if (port)
77 asprintf (&command_line, "%s:%-d", command_line, port);
79 if (verbose > 0)
80 printf ("%s\n", command_line);
82 /* run the command. historically, this plugin ignores output on stderr,
83 * as well as return status of the qstat program */
84 (void)np_runcmd(command_line, &chld_out, NULL, 0);
86 /* sanity check */
87 /* was thinking about running qstat without any options, capturing the
88 -default line, parsing it & making an array of all know server types
89 but thought this would be too much hassle considering this is a tool
90 for intelligent sysadmins (ha). Could put a static array of known
91 server types in a header file but then we'd be limiting ourselves
93 In the end, I figured I'd simply let an error occur & then trap it
94 */
96 if (!strncmp (chld_out.line[0], "unknown option", 14)) {
97 printf (_("CRITICAL - Host type parameter incorrect!\n"));
98 result = STATE_CRITICAL;
99 return result;
100 }
102 p = (char *) strtok (chld_out.line[0], QSTAT_DATA_DELIMITER);
103 while (p != NULL) {
104 ret[i] = p;
105 p = (char *) strtok (NULL, QSTAT_DATA_DELIMITER);
106 i++;
107 if (i >= QSTAT_MAX_RETURN_ARGS)
108 break;
109 }
111 if (strstr (ret[2], QSTAT_HOST_ERROR)) {
112 printf (_("CRITICAL - Host not found\n"));
113 result = STATE_CRITICAL;
114 }
115 else if (strstr (ret[2], QSTAT_HOST_DOWN)) {
116 printf (_("CRITICAL - Game server down or unavailable\n"));
117 result = STATE_CRITICAL;
118 }
119 else if (strstr (ret[2], QSTAT_HOST_TIMEOUT)) {
120 printf (_("CRITICAL - Game server timeout\n"));
121 result = STATE_CRITICAL;
122 }
123 else {
124 printf ("OK: %s/%s %s (%s), Ping: %s ms|%s %s\n",
125 ret[qstat_game_players],
126 ret[qstat_game_players_max],
127 ret[qstat_game_field],
128 ret[qstat_map_field],
129 ret[qstat_ping_field],
130 perfdata ("players", atol(ret[qstat_game_players]), "",
131 FALSE, 0, FALSE, 0,
132 TRUE, 0, TRUE, atol(ret[qstat_game_players_max])),
133 fperfdata ("ping", strtod(ret[qstat_ping_field], NULL), "",
134 FALSE, 0, FALSE, 0,
135 TRUE, 0, FALSE, 0));
136 }
138 return result;
139 }
142 int
143 process_arguments (int argc, char **argv)
144 {
145 int c;
147 int opt_index = 0;
148 static struct option long_opts[] = {
149 {"help", no_argument, 0, 'h'},
150 {"version", no_argument, 0, 'V'},
151 {"verbose", no_argument, 0, 'v'},
152 {"timeout", required_argument, 0, 't'},
153 {"hostname", required_argument, 0, 'H'},
154 {"port", required_argument, 0, 'P'},
155 {"game-type", required_argument, 0, 'G'},
156 {"map-field", required_argument, 0, 'm'},
157 {"ping-field", required_argument, 0, 'p'},
158 {"game-field", required_argument, 0, 'g'},
159 {"players-field", required_argument, 0, 129},
160 {"max-players-field", required_argument, 0, 130},
161 {0, 0, 0, 0}
162 };
164 if (argc < 2)
165 return ERROR;
167 for (c = 1; c < argc; c++) {
168 if (strcmp ("-mf", argv[c]) == 0)
169 strcpy (argv[c], "-m");
170 else if (strcmp ("-pf", argv[c]) == 0)
171 strcpy (argv[c], "-p");
172 else if (strcmp ("-gf", argv[c]) == 0)
173 strcpy (argv[c], "-g");
174 }
176 while (1) {
177 c = getopt_long (argc, argv, "hVvt:H:P:G:g:p:m:", long_opts, &opt_index);
179 if (c == -1 || c == EOF)
180 break;
182 switch (c) {
183 case 'h': /* help */
184 print_help ();
185 exit (STATE_OK);
186 case 'V': /* version */
187 print_revision (progname, revision);
188 exit (STATE_OK);
189 case 'v': /* version */
190 verbose = TRUE;
191 break;
192 case 't': /* timeout period */
193 timeout_interval = atoi (optarg);
194 break;
195 case 'H': /* hostname */
196 if (strlen (optarg) >= MAX_HOST_ADDRESS_LENGTH)
197 die (STATE_UNKNOWN, _("Input buffer overflow\n"));
198 server_ip = optarg;
199 break;
200 case 'P': /* port */
201 port = atoi (optarg);
202 break;
203 case 'G': /* hostname */
204 if (strlen (optarg) >= MAX_INPUT_BUFFER)
205 die (STATE_UNKNOWN, _("Input buffer overflow\n"));
206 game_type = optarg;
207 break;
208 case 'p': /* index of ping field */
209 qstat_ping_field = atoi (optarg);
210 if (qstat_ping_field < 0 || qstat_ping_field > QSTAT_MAX_RETURN_ARGS)
211 return ERROR;
212 break;
213 case 'm': /* index on map field */
214 qstat_map_field = atoi (optarg);
215 if (qstat_map_field < 0 || qstat_map_field > QSTAT_MAX_RETURN_ARGS)
216 return ERROR;
217 break;
218 case 'g': /* index of game field */
219 qstat_game_field = atoi (optarg);
220 if (qstat_game_field < 0 || qstat_game_field > QSTAT_MAX_RETURN_ARGS)
221 return ERROR;
222 break;
223 case 129: /* index of player count field */
224 qstat_game_players = atoi (optarg);
225 if (qstat_game_players_max == 0)
226 qstat_game_players_max = qstat_game_players - 1;
227 if (qstat_game_players < 0 || qstat_game_players > QSTAT_MAX_RETURN_ARGS)
228 return ERROR;
229 break;
230 case 130: /* index of max players field */
231 qstat_game_players_max = atoi (optarg);
232 if (qstat_game_players_max < 0 || qstat_game_players_max > QSTAT_MAX_RETURN_ARGS)
233 return ERROR;
234 break;
235 default: /* args not parsable */
236 usage_va(_("Unknown argument - %s"), optarg);
237 }
238 }
240 c = optind;
241 /* first option is the game type */
242 if (!game_type && c<argc)
243 game_type = strdup (argv[c++]);
245 /* Second option is the server name */
246 if (!server_ip && c<argc)
247 server_ip = strdup (argv[c++]);
249 return validate_arguments ();
250 }
253 int
254 validate_arguments (void)
255 {
256 if (qstat_game_players_max < 0)
257 qstat_game_players_max = 4;
259 if (qstat_game_players < 0)
260 qstat_game_players = 5;
262 if (qstat_game_field < 0)
263 qstat_game_field = 2;
265 if (qstat_map_field < 0)
266 qstat_map_field = 3;
268 if (qstat_ping_field < 0)
269 qstat_ping_field = 5;
271 return OK;
272 }
275 void
276 print_help (void)
277 {
278 print_revision (progname, revision);
280 printf ("Copyright (c) 1999 Ian Cass, Knowledge Matters Limited\n");
281 printf (COPYRIGHT, copyright, email);
283 printf (_("This plugin tests game server connections with the specified host."));
285 print_usage ();
287 printf (_(UT_HELP_VRSN));
289 printf (_("\
290 <game> = Game type that is recognised by qstat (without the leading dash)\n\
291 <ip_address> = The IP address of the device you wish to query\n\
292 [port] = Optional port of which to connect\n\
293 [game_field] = Field number in raw qstat output that contains game name\n\
294 [map_field] = Field number in raw qstat output that contains map name\n\
295 [ping_field] = Field number in raw qstat output that contains ping time\n"));
297 printf (_(UT_TIMEOUT), DEFAULT_SOCKET_TIMEOUT);
299 printf (_("\n\
300 Notes:\n\
301 - This plugin uses the 'qstat' command, the popular game server status query tool .\n\
302 If you don't have the package installed, you will need to download it from\n\
303 http://www.activesw.com/people/steve/qstat.html before you can use this plugin.\n"));
305 printf (_(UT_SUPPORT));
306 }
310 void
311 print_usage (void)
312 {
313 printf ("\
314 Usage: %s <game> <ip_address> [-p port] [-gf game_field] [-mf map_field]\n\
315 [-pf ping_field]\n", progname);
316 }
318 /******************************************************************************
319 *
320 * Test Cases:
321 *
322 * ./check_game --players 7 -p 8 --map 5 qs 67.20.190.61 26000
323 *
324 * qstat -raw , -qs 67.20.190.61
325 * ==> QS,67.20.190.61,Nightmare.fintek.ca,67.20.190.61:26000,3,e2m1,6,0,83,0
326 *
327 * qstat -qs 67.20.190.61
328 * ==> ADDRESS PLAYERS MAP RESPONSE TIME NAME
329 * ==> 67.20.190.61 0/ 6 e2m1 79 / 0 Nightmare.fintek.ca
330 *
331 ******************************************************************************/