1 /* ttt-server.c - tic-tac-toe game server
3 * Copyright © 2005 Carl Worth
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2, or (at your option)
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software Foundation,
17 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19 * Author: Carl Worth <cworth@cworth.org>
22 #include "ttt-server.h"
25 #include "ttt-client.h"
26 #include "ttt-error.h"
27 #include "ttt-socket.h"
30 pthread_mutex_t mutex;
35 ttt_client_t **clients;
41 ttt_server_init (ttt_server_t *server, const char *host, const char *port)
43 pthread_mutex_init (&server->mutex, NULL);
48 server->clients = NULL;
49 server->clients_size = 0;
50 server->num_clients = 0;
53 /* Exported: See ttt-server.h for documentation. */
55 ttt_server_register_client (ttt_server_t *server, ttt_client_t *client)
58 ttt_error_t error = TTT_ERROR_NONE;
61 pthread_mutex_lock (&server->mutex);
63 username = ttt_client_get_username (client);
65 assert (username != NULL);
67 if (username[0] == '\0')
68 return TTT_ERROR_INVALID_NAME;
70 for (i = 0; i < server->num_clients; i++) {
71 if (strcmp (ttt_client_get_username (server->clients[i]), username) == 0) {
72 error = TTT_ERROR_INVALID_NAME;
77 printf ("Client %s has joined.\r\n", username);
79 server->num_clients++;
81 if (server->num_clients > server->clients_size) {
82 if (server->clients_size == 0)
83 server->clients_size = 1;
85 server->clients_size *= 2;
87 server->clients = xrealloc (server->clients,
88 server->clients_size * sizeof (ttt_client_t *));
91 server->clients [server->num_clients - 1] = client;
94 pthread_mutex_unlock (&server->mutex);
99 /* Exported: See ttt-server.h for documentation. */
101 ttt_server_unregister_client (ttt_server_t *server, ttt_client_t *client)
105 pthread_mutex_lock (&server->mutex);
107 for (i = 0; i < server->num_clients; i++)
108 if (server->clients[i] == client)
111 assert (i < server->num_clients);
113 printf ("Client %s has left.\r\n", ttt_client_get_username (client));
115 memmove (&server->clients[i], &server->clients[i+1],
116 (server->num_clients - i - 1) * sizeof (ttt_client_t *));
118 server->num_clients--;
120 pthread_mutex_unlock (&server->mutex);
123 /* Exported: See ttt-server.h for documentation. */
125 ttt_server_broadcast (ttt_server_t *server, const char *message)
129 pthread_mutex_lock (&server->mutex);
131 for (i = 0; i < server->num_clients; i++)
132 ttt_client_send (server->clients[i], message);
134 pthread_mutex_unlock (&server->mutex);
137 /* Exported: See ttt-server.h for documentation. */
139 ttt_server_who (ttt_server_t *server)
144 pthread_mutex_lock (&server->mutex);
146 xasprintf (&response, "WHO");
148 for (i = 0; i < server->num_clients; i++)
149 xasprintf (&response, "%s %s",
151 ttt_client_get_username (server->clients[i]));
153 xasprintf (&response, "%s\r\n", response);
155 pthread_mutex_unlock (&server->mutex);
160 /* Exported: See ttt-server.h for documentation. */
162 ttt_server_statistics (ttt_server_t *server, const char *username, char **response)
164 ttt_bool_t usernamefound = FALSE;
165 const char *client_username;
169 pthread_mutex_lock (&server->mutex);
171 for (i = 0; i < server->num_clients; i++) {
172 client_username = ttt_client_get_username (server->clients[i]);
173 if (strcasecmp (username, client_username) == 0) {
174 usernamefound = TRUE;
175 client_num_wins = ttt_client_get_num_wins (server->clients[i]);
176 xasprintf (response, "STATISTICS %s \"\r\n"
177 "TICTACTOE WINS %d\r\n\"\r\n",
183 pthread_mutex_unlock (&server->mutex);
186 return TTT_ERROR_NO_USER;
188 return TTT_ERROR_NONE;
191 /* Exported: See ttt-server.h for documentation. */
193 ttt_server_verify_username (ttt_server_t *server, const char *username)
195 ttt_bool_t usernamefound = FALSE;
196 const char *client_username;
199 pthread_mutex_lock (&server->mutex);
201 for (i = 0; i < server->num_clients; i++) {
202 client_username = ttt_client_get_username (server->clients[i]);
203 if (strcasecmp (username, client_username) == 0)
204 usernamefound = TRUE;
207 pthread_mutex_unlock (&server->mutex);
210 return TTT_ERROR_NO_USER;
212 return TTT_ERROR_NONE;
215 /* Exported: See ttt-server.h for documentation. */
217 ttt_server_get_host (ttt_server_t *server)
222 /* Exported: See ttt-server.h for documentation. */
224 ttt_server_get_port (ttt_server_t *server)
229 static const char *WELCOME_MESSAGE =
230 "Welcome to ttt-server. The server is currently listening on:\r\n"
234 "To test this, simply connect one or more clients to that host and port.\r\n"
239 "The TTTP (tic-tac-toe protocol) has been partially implemented.\r\n"
240 "The following commands should work: HELO, HELP, MESSAGE, VERSION, QUIT, WHO.\r\n"
244 _ttt_server_accept (void *closure, int client_socket)
246 ttt_server_t *server = closure;
248 ttt_client_new (server, client_socket);
252 _detach_and_write_child_pid_to (const char *filename)
256 /* Use the Unix double-fork trick to detach completely. See
257 * setsid(2) for some details as to why two forks are
261 /* First parent just exits */
270 /* Second parent exits after writing pid */
271 FILE *file = xfopen (filename, "w");
272 fprintf (file, "%d\n", pid);
277 /* Final, detached child returns. */
281 main (int argc, char **argv)
287 ttt_args_parse (&args, argc, argv);
289 if (args.log_file || args.detach) {
291 /* In the detach case, we force redirection to a log file. */
292 if (args.log_file == NULL)
293 args.log_file = "/var/log/ttt-server.log";
294 log_file = fopen (args.log_file, "a");
295 if (log_file == NULL) {
296 printf ("Warning: Failed to open log file %s: %s.\n",
297 args.log_file, strerror (errno));
298 printf ("Logging will be disabled.\n");
301 xdup2 (fileno (log_file), 2);
306 _detach_and_write_child_pid_to (args.pid_file);
308 socket = ttt_socket_create_server (args.host, args.port);
312 printf ("Server started listening on %s:%s\n", args.host, args.port);
314 printf (WELCOME_MESSAGE, args.host, args.port, args.host, args.port);
320 ttt_server_init (&server, args.host, args.port);
323 ttt_socket_accept (socket, _ttt_server_accept, &server);
325 /* We only reach here if something bad happened. */