7 #include <sys/socket.h>
8 #include <sys/select.h>
16 // grace time before sending SIGTERM
17 #define TIME_BEFORE_SIGTERM 5
19 session_info
*sessionlist
= NULL
;
20 int session_count
= 0;
21 pthread_mutex_t sessionlist_mutex
= PTHREAD_MUTEX_INITIALIZER
;
25 session_info
* add_session (int sock
)
27 session_info
*ps
= (session_info
*)malloc(sizeof(session_info
));
28 if (ps
== NULL
) return NULL
;
42 ps
->observer_count
= 0;
44 pthread_mutex_init (&ps
->mutex
, NULL
);
45 ps
->last_activity
= time(NULL
);
48 pthread_mutex_lock (&sessionlist_mutex
);
51 if (sessionlist
== NULL
)
57 session_info
*p
= sessionlist
;
58 while (p
->next
!= NULL
) p
= p
->next
;
64 pthread_mutex_unlock (&sessionlist_mutex
);
71 void remove_session (session_info
*sess
)
75 pthread_mutex_lock (&sessionlist_mutex
);
76 pthread_mutex_lock (&sess
->mutex
);
78 for (s
= sessionlist
; s
!= NULL
; s
= s
->next
)
80 // remove sess from the list
83 if (s
->next
) s
->next
->prev
= s
->prev
;
84 if (s
->prev
) s
->prev
->next
= s
->next
;
86 if (sessionlist
== sess
) sessionlist
= sess
->next
;
89 // remove socket from observers
93 // no need to lock s->mutex because add_observer() is blocked by sessionlist_mutex
94 for (i
= 0; i
< s
->observer_count
; i
++)
96 if (s
->observers
[i
] == sess
->socket
)
98 s
->observers
[i
--] = s
->observers
[s
->observer_count
--];
104 pthread_mutex_unlock (&sessionlist_mutex
);
106 pthread_mutex_unlock (&sess
->mutex
);
107 pthread_mutex_destroy (&sess
->mutex
);
114 void stop_observing (session_info
*sess
)
118 pthread_mutex_lock (&sessionlist_mutex
);
120 for (s
= sessionlist
; s
!= NULL
; s
= s
->next
)
122 // remove socket from observers
124 // no need to lock s->mutex because add_observer() is blocked by sessionlist_mutex
125 for (i
= 0; i
< s
->observer_count
; i
++)
127 if (s
->observers
[i
] == sess
->socket
)
129 s
->observers
[i
--] = s
->observers
[s
->observer_count
--];
134 pthread_mutex_unlock (&sessionlist_mutex
);
139 int add_observer (int n
, int sock
)
144 pthread_mutex_lock (&sessionlist_mutex
);
146 for (s
= sessionlist
; s
!= NULL
; s
= s
->next
)
148 if (s
->game
[0] == 0) continue;
149 if (i
++ != n
) continue;
151 pthread_mutex_lock (&s
->mutex
);
152 s
->observers
= (int*)realloc(s
->observers
, (s
->observer_count
+ 1) * sizeof(int));
153 s
->observers
[s
->observer_count
++] = sock
;
154 pthread_mutex_unlock (&s
->mutex
);
159 if (s
->term
!= NULL
) term_copy_data (s
->term
, sock
);
161 pthread_mutex_unlock (&sessionlist_mutex
);
168 * close everything that is not neccessary before running a game in a child process
170 static void close_server_things (void)
174 extern int server_socket
;
175 close (server_socket
);
177 // local stdio is not used
185 static void send_to_observers (session_info
*sess
, const uint8_t *buf
, int len
)
189 pthread_mutex_lock (&sess
->mutex
);
190 for (i
= 0; i
< sess
->observer_count
; i
++)
192 send (sess
->observers
[i
], buf
, len
, 0);
194 pthread_mutex_unlock (&sess
->mutex
);
199 static void remove_observers (session_info
*sess
)
201 const char msg
[] = "\033[0m\033[40;37m\r\nThe observed game was disconnected\r\n";
202 send_to_observers (sess
, msg
, sizeof(msg
) - 1);
204 pthread_mutex_lock (&sess
->mutex
);
205 free (sess
->observers
);
206 sess
->observers
= NULL
;
207 sess
->observer_count
= 0;
208 pthread_mutex_unlock (&sess
->mutex
);
213 static void close_game (session_info
*sess
)
215 // check if the game has terminated already
216 if (waitpid(sess
->child_pid
, NULL
, WNOHANG
) != 0)
222 // wait some time to allow the game to handle the last input
223 sleep (TIME_BEFORE_SIGTERM
);
225 // if the game is still running, send SIGTERM
226 if (waitpid(sess
->child_pid
, NULL
, WNOHANG
) == 0)
228 kill (sess
->child_pid
, SIGTERM
);
235 #define BUF_SIZE 16*1024
236 static void play_game (session_info
*sess
)
238 uint8_t buf
[BUF_SIZE
];
240 int sn
= (sess
->socket
> sess
->pty_master
) ? sess
->socket
: sess
->pty_master
;
242 sess
->term
= init_term(sess
->term_wid
, sess
->term_hgt
);
247 FD_SET (sess
->pty_master
, &rfds
);
248 FD_SET (sess
->socket
, &rfds
);
250 // wait until game prints something to output
251 // or player sends something over the net
252 if (select(sn
+ 1, &rfds
, NULL
, NULL
, NULL
) == -1)
258 // got output from the running game
259 if (FD_ISSET(sess
->pty_master
, &rfds
))
261 int nr
= read(sess
->pty_master
, buf
, BUF_SIZE
);
264 // send it to the player
265 send (sess
->socket
, buf
, nr
, 0);
268 if (sess
->term
!= NULL
) term_process (sess
->term
, buf
, nr
);
270 // duplicate data for the observers
271 send_to_observers (sess
, buf
, nr
);
274 // got input from the player trough the net
275 if (FD_ISSET(sess
->socket
, &rfds
))
277 int nr
= recv(sess
->socket
, buf
, BUF_SIZE
, 0);
280 nr
= translate_telnet_input(buf
, nr
, sess
);
281 if (nr
> 0) write (sess
->pty_master
, buf
, nr
);
283 // update activity timestamp
284 sess
->last_activity
= time(NULL
);
292 static void exec_game (session_info
*sess
, const game_info
*game
)
294 const char user_str
[] = "$USER$";
295 char buf
[BUF_SIZE
]; // assume BUF_SIZE > GAME_COMMAND_LEN + USER_NAME_LEN
300 // change to the specified directory
301 if (game
->dir
[0]) chdir (game
->dir
);
303 // copy command line to bufer, substituting user name
304 for (i
= b
= 0; game
->command
[i
] != 0; i
++, b
++)
306 if (strncmp(&game
->command
[i
], user_str
, sizeof(user_str
) - 1) == 0)
308 const char *u
= sess
->user_name
;
309 while ((buf
[b
++] = *(u
++)));
310 i
+= sizeof(user_str
) - 1 - 1;
315 buf
[b
] = game
->command
[i
];
320 // break buf[] into zero-terminated strings, filling arg[] with pointers to them
321 // TODO: add quotes or \ to escape spaces
323 for (b
= 0; buf
[b
] != 0; b
++)
328 if (buf
[b
+ 1] != 0 && a
< MAX_ARGS
- 1)
330 arg
[a
++] = &buf
[b
+ 1];
337 execvp (arg
[0], arg
);
342 void run_game (session_info
*sess
, const game_info
*game
)
344 int pty_master
, pty_slave
;
349 ws
.ws_col
= sess
->term_wid
;
350 ws
.ws_row
= sess
->term_hgt
;
351 if (openpty(&pty_master
, &pty_slave
, NULL
, NULL
, &ws
) == -1)
353 perror ("openpty failed");
357 if ((pid
= fork()) < 0)
359 perror ("fork failed");
368 close_server_things ();
370 dup2 (pty_slave
, 0); // reassign stdin to pty
371 dup2 (pty_slave
, 1); // reassign stdout to pty
372 dup2 (pty_slave
, 2); // reassign stderr to pty
373 // these are not needed anymore
377 exec_game (sess
, game
);
379 // exec should never return
380 perror ("child failed");
389 sess
->pty_master
= pty_master
;
390 sess
->child_pid
= pid
;
391 strcpy (sess
->game
, game
->name
);
395 remove_observers (sess
);
399 sess
->pty_master
= -1;
405 void close_sessions (void)
408 for (s
= sessionlist
; s
!= NULL
; s
= s
->next
)
410 if (s
->pty_master
!= -1) close (s
->pty_master
);
417 void release_sessions (void)
420 for (s
= sessionlist
; s
!= NULL
; s
= s
->next
)
423 pthread_mutex_destroy (&s
->mutex
);