/*- * wmQStat - WindowMaker/AfterStep/BB/FB/Waimea dockable front-end to * Steve Jankowski's qstat (http://www.qstat.org/). * * Copyright (c) 2003-2005 Alexey Dokuchaev. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $Id: srv_proc.c,v 1.14 2005/02/25 12:28:25 danfe Exp $ */ #include #include #include #define _SRV_PROC #include "proto.h" /* * We query qstat(1) with the following arguments: * -P fetch player information (duh!) * -sort F sort players by frags * -nh do not display header line * -ts display time in seconds (this way, * time can always be parsed simply as %d) * -ncn display color numbers instead of names * (again, numbers are easier to handle) * -retry 10 retry ten times (probably useless) */ #define QSTAT_ARGS "qstat -P -sort F -nh -ts -ncn -retry 10 -" #define MAX_CMD_LINE sizeof(QSTAT_ARGS) + MAX_SERVER_TYPE + 1 + \ MAX_SERVER_ADDR //#define SRV_PLAYER_TEAMS (1 << 2) char cmd[MAX_CMD_LINE + 1] = QSTAT_ARGS; /* * Allocate memory and make initial call to obtain_stats(). * Return NULL upon failure (malloc errors). */ struct server * add_server(const char *type, const char *address) { struct server *srv; if (srv = malloc(sizeof(struct server))) { strncpy(srv->type, type, MAX_SERVER_TYPE); strncpy(srv->address, address, MAX_SERVER_ADDR); srv->players = NULL; if (!strncmp(srv->type, "q2s", MAX_SERVER_TYPE) || !strncmp(srv->type, "q3s", MAX_SERVER_TYPE) || !strncmp(srv->type, "rws", MAX_SERVER_TYPE) || !strncmp(srv->type, "efs", MAX_SERVER_TYPE) || !strncmp(srv->type, "sns", MAX_SERVER_TYPE) || !strncmp(srv->type, "sgs", MAX_SERVER_TYPE) || !strncmp(srv->type, "kps", MAX_SERVER_TYPE) || !strncmp(srv->type, "hrs", MAX_SERVER_TYPE) || !strncmp(srv->type, "sfs", MAX_SERVER_TYPE) || !strncmp(srv->type, "jk3s", MAX_SERVER_TYPE) || !strncmp(srv->type, "sgs", MAX_SERVER_TYPE) || !strncmp(srv->type, "ut2s", MAX_SERVER_TYPE) || !strncmp(srv->type, "uns", MAX_SERVER_TYPE) || !strncmp(srv->type, "gps", MAX_SERVER_TYPE)) srv->query = query_q2_server; else if (!strncmp(srv->type, "hls", MAX_SERVER_TYPE) || !strncmp(srv->type, "hl2s", MAX_SERVER_TYPE) || !strncmp(srv->type, "rss", MAX_SERVER_TYPE) || !strncmp(srv->type, "sas", MAX_SERVER_TYPE) || !strncmp(srv->type, "fcs", MAX_SERVER_TYPE)) srv->query = query_hl_server; else if (!strncmp(srv->type, "eye", MAX_SERVER_TYPE) || !strncmp(srv->type, "gs2", MAX_SERVER_TYPE) || !strncmp(srv->type, "dm3s", MAX_SERVER_TYPE)) srv->query = query_eye_server; else if (!strncmp(srv->type, "d3g", MAX_SERVER_TYPE) || !strncmp(srv->type, "d3s", MAX_SERVER_TYPE) || !strncmp(srv->type, "d3p", MAX_SERVER_TYPE)) srv->query = query_dc3_server; else if (!strncmp(srv->type, "qws", MAX_SERVER_TYPE) || !strncmp(srv->type, "hws", MAX_SERVER_TYPE)) srv->query = query_qw_server; else if (!strncmp(srv->type, "qs", MAX_SERVER_TYPE) || !strncmp(srv->type, "h2s", MAX_SERVER_TYPE)) srv->query = query_q_server; else if (!strncmp(srv->type, "tbs", MAX_SERVER_TYPE)) srv->query = query_trb_server; else if (!strncmp(srv->type, "t2s", MAX_SERVER_TYPE)) srv->query = query_trb2_server; else if (!strncmp(srv->type, "bfs", MAX_SERVER_TYPE)) srv->query = query_bfris_server; else if (!strncmp(srv->type, "grs", MAX_SERVER_TYPE)) srv->query = query_ghrec_server; obtain_stats(srv); } return (srv); } /* * Deallocate all memory server and its clients occupy. * Normally called at the graceful end, or when further allocations * failed for some reason (the latter should not happen though). */ void kill_server(struct server *srv) { free(srv->players); free(srv); } /* * Get players' statistics from server. We are currently interested * only in player's name, frags, and time (but preferably ping, of * course) but later may want more (things like, say, team; though I * somewhat doubt it). */ int obtain_stats(struct server *srv) { FILE *p; int i, cmpl; /* current max players */ cmd[sizeof(QSTAT_ARGS) - 1] = '\0'; strncat(cmd, srv->type, MAX_SERVER_TYPE); strncat(cmd, " ", 1); strncat(cmd, srv->address, MAX_SERVER_ADDR); *srv->map = '\0'; if (p = popen(cmd, "r")) { fscanf(p, "%*s%d/%d%16s%*[^\n]", &srv->npl, &cmpl, srv->map); if (!srv->map) { /* server is down or other error. don't free memory, it may come up later */ pclose(p); return (1); } if (!srv->players) { if (!(srv->players = malloc(cmpl * sizeof(struct player)))) shut_down(srv, p); srv->maxpl = cmpl; } if (cmpl > srv->maxpl) { /* server increased maxclients */ if (!realloc(srv->players, cmpl * sizeof(struct player))) shut_down(srv, p); srv->maxpl = cmpl; } for (i = 0; i < srv->npl; i++) { /* reset initial values to zero, and ping/time to -1 (meaning this statistic is not available for certain server type */ srv->players[i].frags = *srv->players[i].name = 0; srv->players[i].ping = srv->players[i].time = -1; (*srv->query)(p, &srv->players[i]); } pclose(p); return (0); } return (1); } /* * When in disgrace, we call this one. ;-) * We probably could avoid exit()ing here, but the way * it's done now simplifies things for us. * XXX Feel free to rework. XXX */ static void shut_down(struct server *srv, FILE *p) { kill_server(srv); pclose(p); exit(1); }