check_game.c 9.7 KB

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