8 #include <sys/socket.h>
9 #include <netinet/in.h>
10 #include <arpa/inet.h>
21 #define CONFIG_FILE "config"
23 // socket used to listen for new connections
24 int server_socket
= -1;
26 server_config config
= {"ee", 3456, 3, 5, -1, -1, "", ""};
31 * scan system users for the specified name
34 static int find_uid (const char *name
)
39 // rewind to the first entry
43 while ((pwd
= getpwent()) != NULL
)
45 if (strcmp(name
, pwd
->pw_name
) == 0) return pwd
->pw_uid
;
55 * scan system groups for the specified name
58 static int find_gid (const char *name
)
63 // rewind to the first entry
67 while ((grp
= getgrent()) != NULL
)
69 if (strcmp(name
, grp
->gr_name
) == 0) return grp
->gr_gid
;
78 static int parse_config_line (char *buf
, int ln
)
84 // find value after ':'
85 val
= strchr(buf
, ':');
88 write_log (LOG_ERROR
, CONFIG_FILE
":%d: parse error", ln
);
92 // skip leading spaces
93 do val
++; while (isspace(val
[0]));
96 if (val
[len
- 1] == '\n') val
[--len
] = 0; // subtract 1 for \n at the end
97 if (len
>= CONFIG_LEN
)
99 write_log (LOG_ERROR
, CONFIG_FILE
":%d: value is too long (max %d)", ln
, CONFIG_LEN
);
103 if (strncmp(buf
, "port:", 5) == 0)
105 config
.port
= atoi(val
);
109 if (strncmp(buf
, "backlog:", 8) == 0)
111 config
.backlog
= atoi(val
);
115 if (strncmp(buf
, "editor:", 7) == 0)
117 memcpy (config
.editor
, val
, len
);
121 if (strncmp(buf
, "kill_delay:", 11) == 0)
123 config
.delay_before_kill
= atoi(val
);
127 if (strncmp(buf
, "user:", 5) == 0)
129 if ((config
.uid
= find_uid(val
)) == -1)
131 write_log (LOG_ERROR
, CONFIG_FILE
":%d: invalid user %s", ln
, val
);
137 if (strncmp(buf
, "group:", 6) == 0)
139 if ((config
.gid
= find_gid(val
)) == -1)
141 write_log (LOG_ERROR
, CONFIG_FILE
":%d: invalid group %s", ln
, val
);
147 if (strncmp(buf
, "stats:", 6) == 0)
149 memcpy (config
.game_stat_file
, val
, len
);
153 if (strncmp(buf
, "log:", 4) == 0)
155 memcpy (config
.game_log_file
, val
, len
);
159 write_log (LOG_ERROR
, CONFIG_FILE
":%d: unknown parameter", ln
);
165 #define CFG_BUF_SIZE (CONFIG_LEN + 16)
166 static int load_config (void)
168 char buf
[CFG_BUF_SIZE
];
173 if ((f
= fopen(CONFIG_FILE
, "r")) == NULL
)
175 write_log (LOG_ERROR
, "can't open config file: " CONFIG_FILE
);
182 if (fgets(buf
, CFG_BUF_SIZE
, f
) == NULL
) break;
184 // skip comments and empty strings
185 if (buf
[0] == '#' || buf
[0] == '\n' || buf
[0] == 0)
191 if (parse_config_line(buf
, ln
) == -1)
206 static void sigchld_handler (int s
)
208 while (waitpid(-1, NULL
, WNOHANG
) > 0);
213 static void sigterm_handler (int s
)
215 write_log (LOG_INFO
, "initiating shutdown");
217 // do not accept new connections
218 close (server_socket
);
220 // terminate all running games
223 /* we do not really care if spawned threads have been finished
224 because all threads will be forced to close anyway when the main process exits
225 it is a little bit dirty, but is simple and works */
227 extern pthread_mutex_t sessionlist_mutex
;
228 pthread_mutex_destroy (&sessionlist_mutex
);
230 // close and release all connections
234 write_log (LOG_INFO
, "shutdown complete");
240 static void init_signals (void)
245 // prevent terminated child processes from becoming zombies
246 sa
.sa_handler
= sigchld_handler
;
247 sigemptyset (&sa
.sa_mask
);
248 sa
.sa_flags
= SA_RESTART
| SA_NOCLDSTOP
;
249 if (sigaction(SIGCHLD
, &sa
, NULL
) == -1)
251 write_log (LOG_FATAL
, "error setting SIGCHLD handler");
256 // gracefully close all runing games and exit upon request
257 sa
.sa_handler
= sigterm_handler
;
258 sigemptyset (&sa
.sa_mask
);
259 sa
.sa_flags
= SA_RESTART
;
260 if (sigaction(SIGTERM
, &sa
, NULL
) == -1)
262 write_log (LOG_FATAL
, "error setting SIGTERM handler");
265 // use the same handler for SIGINT
266 if (sigaction(SIGINT
, &sa
, NULL
) == -1)
268 write_log (LOG_FATAL
, "error setting SIGINT handler");
272 // SIGPIPE - ignore it (check return codes for errors instead)
273 sa
.sa_handler
= SIG_IGN
;
274 sigemptyset (&sa
.sa_mask
);
275 if (sigaction(SIGPIPE
, &sa
, NULL
) == -1)
277 write_log (LOG_FATAL
, "error setting SIGPIPE to ignore");
284 static int open_socket (int port
)
286 int sockfd
; // listen on sock_fd
287 struct sockaddr_in my_addr
; // my address information
290 if ((sockfd
= socket(AF_INET
, SOCK_STREAM
, 0)) == -1)
292 write_log (LOG_FATAL
, "unable to create socket");
296 if (setsockopt(sockfd
, SOL_SOCKET
, SO_REUSEADDR
, &yes
, sizeof(int)) == -1)
298 write_log (LOG_FATAL
, "unable to set socket options");
303 my_addr
.sin_family
= AF_INET
; // host byte order
304 my_addr
.sin_port
= htons(port
); // short, network byte order
305 my_addr
.sin_addr
.s_addr
= INADDR_ANY
; // automatically fill with my IP
306 memset (&(my_addr
.sin_zero
), 0, 8); // zero the rest of the struct
308 if (bind(sockfd
, (struct sockaddr
*)&my_addr
, sizeof(struct sockaddr
)) == -1)
310 write_log (LOG_FATAL
, "unable to bind socket to port %d", port
);
315 if (listen(sockfd
, config
.backlog
) == -1)
317 write_log (LOG_FATAL
, "unable to listen on socket");
335 * Handle newly connected client in a thread
337 static void* connection_thread (void *data
)
341 int sock
= ((thread_data
*)data
)->sock
;
344 send_telnet_init (sock
);
346 sess
= add_session(sock
);
350 if (sess
->user_name
[0]) write_log (LOG_DEBUG
, "user %s disconnected", sess
->user_name
);
351 else write_log (LOG_DEBUG
, "anonymous user disconnected");
353 remove_session (sess
);
360 static void handle_connect (int new_fd
)
362 pthread_attr_t thread_attr
;
367 if ((data
= malloc(sizeof(thread_data
))) == NULL
)
370 write_log (LOG_ERROR
, "can not handle connection: not enough memory");
377 pthread_attr_init (&thread_attr
);
378 pthread_attr_setdetachstate (&thread_attr
, PTHREAD_CREATE_DETACHED
);
379 pthread_attr_setscope (&thread_attr
, PTHREAD_SCOPE_PROCESS
);
381 // handle new connection in a thread
382 if (pthread_create(&th
, &thread_attr
, connection_thread
, data
) != 0)
384 write_log (LOG_ERROR
, "can not create thread to handle connection");
397 write_log (LOG_INFO
, "starting");
399 if (load_config() == -1) return 2;
403 server_socket
= open_socket(config
.port
);
404 if (server_socket
== -1) return 1;
409 if (config
.gid
!= -1)
411 if (setgid(config
.gid
) != 0)
413 write_log (LOG_FATAL
, "can not set group id to %d\n", config
.gid
);
418 if (config
.uid
!= -1)
420 if (setuid(config
.uid
) != 0)
422 write_log (LOG_FATAL
, "can not set user id to %d\n", config
.uid
);
427 // had the user lost his mind?
430 write_log (LOG_FATAL
, "running this software as root is strictly forbidden");
434 // detach from the terminal in a daemon style init
435 if (fork()) return 0;
440 write_log (LOG_INFO
, "init complete, ready to serve");
445 struct sockaddr_in their_addr
; // connector's address information
446 socklen_t sin_size
= sizeof(struct sockaddr_in
);
449 // got a new connection
450 if ((new_fd
= accept(server_socket
, (struct sockaddr
*)&their_addr
, &sin_size
)) == -1)
452 write_log (LOG_ERROR
, "unable to accept connection");
456 write_log (LOG_DEBUG
, "got connection from %s\n", inet_ntoa(their_addr
.sin_addr
));
458 handle_connect (new_fd
);
461 // code here is never supposed to execute
462 // the program terminates via SIGTERM handler