]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | * $Source$ | |
3 | * $Header$ | |
4 | */ | |
5 | ||
6 | #ifndef lint | |
7 | static char *rcsid_tsr_c = "$Header$"; | |
8 | #endif lint | |
9 | ||
10 | /************************************************************************/ | |
11 | /* | |
12 | /* tsr (test server) | |
13 | /* ----------------- | |
14 | /* | |
15 | /* Author: Noah Mendelsohn (IBM T.J. Watson Research and MIT Project | |
16 | /* Athena) | |
17 | /* | |
18 | /* Copyright: 1986 MIT Project Athena | |
19 | /* | |
20 | /************************************************************************/ | |
21 | /* | |
22 | /* PURPOSE | |
23 | /* ------- | |
24 | /* | |
25 | /* A GDB server program demonstrating techniques for asynchronously | |
26 | /* communicating with an arbitrary number of clients from a single | |
27 | /* Unix server process. This server accepts GDB connections from | |
28 | /* clients as requests come in (up to the arbitrary maximum | |
29 | /* MAXCLIENTS.) On each connection, it receives a stream of integers, | |
30 | /* which it interprets as ASCII characters. The characters are | |
31 | /* converted to uppercase, and then sent back to the client from | |
32 | /* which they came. | |
33 | /* | |
34 | /* All of this is done completely asynchronously. No client is | |
35 | /* locked out while characters are being echoed to another, and | |
36 | /* new connections are accepted at any time. | |
37 | /* | |
38 | /* NOTES | |
39 | /* ----- | |
40 | /* | |
41 | /* 1) The complete state of each client is kept in the array | |
42 | /* named client. The client[i].state variable indicates whether | |
43 | /* client i is active, and if so, the client[i].action variable | |
44 | /* indicates what kind of asynchronous activity the client is | |
45 | /* engaged in. Note that these are local conventions, having | |
46 | /* nothing to do with GDB or its interfaces. | |
47 | /* | |
48 | /* 2) Communication to each client is done over its connection, | |
49 | /* named client[i].con. | |
50 | /* | |
51 | /* 3) There is at most one asynchronous activity pending to | |
52 | /* each client at any given time, and its state is tracked | |
53 | /* in the variable named client[i].pending_op. The operation | |
54 | /* may be a send, a receive, or an accept, depending on | |
55 | /* the contents of client[i].action. These operations are | |
56 | /* allocated when the server starts up, and then re-used | |
57 | /* repeatedly. They are the GDB analog of a lightweight process, | |
58 | /* which is activated when queued on a connection. | |
59 | /* | |
60 | /* 4) A special form of connection and a special listening operation | |
61 | /* are used for asynchronously listening for new connection | |
62 | /* requests. These are 'listencon' and 'listenop' respectively. | |
63 | \f/* | |
64 | /* 5) GDB includes a special form of select which waits for | |
65 | /* completion of operations as well as for activity on user | |
66 | /* specified file descriptors. The list of operations to be | |
67 | /* monitored is stored in the variable | |
68 | /* named op_list. The call to op_select_any hangs until one | |
69 | /* or more of these operations complete, then terminates. | |
70 | /* | |
71 | /* 6) The main server loop acts on any new connection requests, | |
72 | /* processes any newly completed activity on the active | |
73 | /* clients, then drops into op_select_any to allow asynchronous | |
74 | /* activities to progress. | |
75 | /* | |
76 | /* | |
77 | /************************************************************************/ | |
78 | ||
79 | #include <stdio.h> | |
80 | #include "gdb.h" | |
81 | ||
82 | extern int errno; | |
83 | ||
84 | \f | |
85 | /************************************************************************/ | |
86 | /* | |
87 | /* DECLARATIONS | |
88 | /* | |
89 | /************************************************************************/ | |
90 | ||
91 | #define MAXCLIENTS 10 | |
92 | ||
93 | /*----------------------------------------------------------*/ | |
94 | /* | |
95 | /* State of each possible client | |
96 | /* | |
97 | /*----------------------------------------------------------*/ | |
98 | ||
99 | struct client { | |
100 | int state; /* state of this client */ | |
101 | /* descriptor */ | |
102 | #define CL_DEAD 1 /* client not started */ | |
103 | #define CL_STARTING 2 /* accepted, reply ongoing */ | |
104 | #define CL_UP 3 /* ready to go */ | |
105 | int action; /* what are we doing now */ | |
106 | #define CL_RECEIVE 4 /* waiting for a packet */ | |
107 | #define CL_SEND 5 /* sending a packet */ | |
108 | #define CL_ACCEPT 6 /* sending a reply */ | |
109 | CONNECTION con; /* connection to this */ | |
110 | /* client, if any */ | |
111 | OPERATION pending_op; /* pending operation */ | |
112 | /* on this connection, */ | |
113 | /* if any */ | |
114 | int data; /* the character to echo */ | |
115 | /* goes here, expressed as */ | |
116 | /* an int */ | |
117 | }; | |
118 | ||
119 | struct client client[MAXCLIENTS]; | |
120 | ||
121 | /*----------------------------------------------------------*/ | |
122 | /* | |
123 | /* Connections and operations for listening for | |
124 | /* new clients. | |
125 | /* | |
126 | /*----------------------------------------------------------*/ | |
127 | ||
128 | CONNECTION listencon; /* listen on this */ | |
129 | /* connection */ | |
130 | OPERATION listenop; /* this operation is used */ | |
131 | /* repeatedly for listening */ | |
132 | /* for new clients */ | |
133 | ||
134 | int nextcl = 0; /* index of the next client */ | |
135 | /* we'll accept */ | |
136 | ||
137 | \f /*----------------------------------------------------------*/ | |
138 | /* | |
139 | /* Miscellaneous variables used in acquiring connections. | |
140 | /* These are ignored in a simple server like this; a | |
141 | /* more sophisticated server might want to validate the | |
142 | /* names of its clients before accepting connections. | |
143 | /* | |
144 | /*----------------------------------------------------------*/ | |
145 | ||
146 | TUPLE client_tuple; /* client request goes */ | |
147 | /* here */ | |
148 | char otherside[100]; | |
149 | int othersize; | |
150 | ||
151 | ||
152 | \f | |
153 | /************************************************************************/ | |
154 | /* | |
155 | /* MAIN | |
156 | /* | |
157 | /************************************************************************/ | |
158 | ||
159 | int | |
160 | main(argc, argv) | |
161 | int argc; | |
162 | char *argv[]; | |
163 | { | |
164 | /*----------------------------------------------------------*/ | |
165 | /* | |
166 | /* LOCAL VARIABLES | |
167 | /* | |
168 | /*----------------------------------------------------------*/ | |
169 | ||
170 | register int i; /* loop index */ | |
171 | LIST_OF_OPERATIONS op_list; /* for op_select_any */ | |
172 | ||
173 | /*----------------------------------------------------------*/ | |
174 | /* | |
175 | /* Check parameters | |
176 | /* | |
177 | /*----------------------------------------------------------*/ | |
178 | ||
179 | if (argc != 2) { | |
180 | fprintf(stderr,"Correct form is %s <servicename>\n", | |
181 | argv[0]); | |
182 | exit(4); | |
183 | } | |
184 | ||
185 | /*----------------------------------------------------------*/ | |
186 | /* | |
187 | /* Initialize | |
188 | /* | |
189 | /*----------------------------------------------------------*/ | |
190 | ||
191 | gdb_init(); /* set up gdb */ | |
192 | init_clients(); /* null the client states */ | |
193 | do_listen(argv[1]); /* start the listening */ | |
194 | /* connection and queue */ | |
195 | /* a listening operation */ | |
196 | make_oplist(&op_list); /* create wait list */ | |
197 | ||
198 | /*----------------------------------------------------------*/ | |
199 | /* | |
200 | /* Loop forever taking care of business. | |
201 | /* | |
202 | /* 1) If any new connection requests have come in, | |
203 | /* accept them. | |
204 | /* | |
205 | /* 2) For each client on which some activity is newly | |
206 | /* completed, take care of it. | |
207 | /* | |
208 | /*----------------------------------------------------------*/ | |
209 | ||
210 | while (TRUE) { | |
211 | if (OP_DONE(listenop)) | |
212 | new_connection(); | |
213 | for (i=0; i<MAXCLIENTS; i++) { | |
214 | if (OP_DONE(client[i].pending_op)) | |
215 | do_client(i); | |
216 | } | |
217 | if(op_select_any(op_list, 0, NULL, NULL, NULL, NULL)==(-1)) { | |
218 | perror("op_select_any returned error"); | |
219 | exit(32); | |
220 | } | |
221 | } | |
222 | } | |
223 | \f | |
224 | /************************************************************************/ | |
225 | /* | |
226 | /* do_client | |
227 | /* | |
228 | /* An operation has completed on the specified client. | |
229 | /* | |
230 | /************************************************************************/ | |
231 | ||
232 | int | |
233 | do_client(id) | |
234 | int id; | |
235 | { | |
236 | register struct client *cp = &(client[id]); | |
237 | ||
238 | /* | |
239 | * If there has been an error, shutdown the client. | |
240 | */ | |
241 | connection_perror(cp->con, "Unix error on send or receive"); | |
242 | /* print error if any */ | |
243 | if (connection_status(cp->con) != CON_UP || | |
244 | OP_STATUS(cp->pending_op) == OP_CANCELLED) { | |
245 | sever_connection(cp->con); | |
246 | reset_operation(cp->pending_op); | |
247 | cp->state = CL_DEAD; | |
248 | cp->action = 0; | |
249 | return; | |
250 | } | |
251 | /* | |
252 | * The operation completed successfully. Figure out what it was | |
253 | * and do the right thing. | |
254 | */ | |
255 | switch (cp->action) { | |
256 | case CL_ACCEPT: | |
257 | case CL_SEND: | |
258 | start_receiving_object(cp->pending_op, cp->con, | |
259 | (char *)&cp->data, | |
260 | INTEGER_T); | |
261 | cp->action = CL_RECEIVE; | |
262 | break; | |
263 | case CL_RECEIVE: | |
264 | if (cp->data >= 'a' && cp->data <= 'z') | |
265 | cp->data += 'A'-'a'; /* upcase the response */ | |
266 | start_sending_object(cp->pending_op, cp->con, | |
267 | (char *)&cp->data, | |
268 | INTEGER_T); | |
269 | cp->action = CL_SEND; | |
270 | } | |
271 | } | |
272 | \f | |
273 | /************************************************************************/ | |
274 | /* | |
275 | /* init_clients | |
276 | /* | |
277 | /************************************************************************/ | |
278 | ||
279 | int | |
280 | init_clients() | |
281 | { | |
282 | register struct client *c; | |
283 | ||
284 | for (c=client; c<client+MAXCLIENTS; c++){ | |
285 | c->state = CL_DEAD; | |
286 | c->action = 0; | |
287 | c->con = NULL; | |
288 | c->pending_op = create_operation(); | |
289 | reset_operation(c->pending_op); | |
290 | } | |
291 | } | |
292 | ||
293 | ||
294 | ||
295 | /************************************************************************/ | |
296 | /* | |
297 | /* make_oplist | |
298 | /* | |
299 | /************************************************************************/ | |
300 | ||
301 | int | |
302 | make_oplist(oplp) | |
303 | LIST_OF_OPERATIONS *oplp; | |
304 | { | |
305 | /* | |
306 | * ugh! we've got to fix create_list_of_operations to be | |
307 | * more flexible!! | |
308 | */ | |
309 | ||
310 | *oplp = create_list_of_operations(MAXCLIENTS+1, listenop, | |
311 | client[0].pending_op, | |
312 | client[1].pending_op, | |
313 | client[2].pending_op, | |
314 | client[3].pending_op, | |
315 | client[4].pending_op, | |
316 | client[5].pending_op, | |
317 | client[6].pending_op, | |
318 | client[7].pending_op, | |
319 | client[8].pending_op, | |
320 | client[9].pending_op); | |
321 | } | |
322 | \f/************************************************************************/ | |
323 | /* | |
324 | /* do_listen | |
325 | /* | |
326 | /* Do the one time setup for listening for clients, and | |
327 | /* also start a listen for an actual client. | |
328 | /* | |
329 | /************************************************************************/ | |
330 | ||
331 | int | |
332 | do_listen(service) | |
333 | char *service; | |
334 | { | |
335 | ||
336 | /*----------------------------------------------------------*/ | |
337 | /* | |
338 | /* Make a listening connection | |
339 | /* | |
340 | /*----------------------------------------------------------*/ | |
341 | ||
342 | fprintf(stderr, "Server creating listening connection\n"); | |
343 | listencon = create_listening_connection(service); | |
344 | ||
345 | if (listencon == NULL || connection_status(listencon) != CON_UP) { | |
346 | if(connection_status(listencon) == CON_STOPPING) { | |
347 | connection_perror(listencon, | |
348 | "Unix error creating listening connection"); | |
349 | } | |
350 | fprintf(stderr,"tsr: could not create listening connection\n"); | |
351 | exit (4); | |
352 | } | |
353 | ||
354 | /*----------------------------------------------------------*/ | |
355 | /* | |
356 | /* On that connection, put up an operation to listen | |
357 | /* for our first client. | |
358 | /* | |
359 | /*----------------------------------------------------------*/ | |
360 | ||
361 | listenop = create_operation(); | |
362 | ||
363 | othersize = sizeof(otherside); | |
364 | ||
365 | start_accepting_client(listencon, listenop, &(client[nextcl].con), | |
366 | (char *)otherside, | |
367 | &othersize, &client_tuple); | |
368 | ||
369 | } | |
370 | \f | |
371 | /************************************************************************/ | |
372 | /* | |
373 | /* new_connection | |
374 | /* | |
375 | /* We have just gotten a connection for client nextcl. | |
376 | /* | |
377 | /************************************************************************/ | |
378 | ||
379 | int | |
380 | new_connection() | |
381 | { | |
382 | register struct client *cp = &client[nextcl]; | |
383 | /* | |
384 | * Make sure there's been no error | |
385 | */ | |
386 | if(connection_status(listencon) != CON_UP) { | |
387 | connection_perror(listencon, "Unix error on listening connection"); | |
388 | fprintf(gdb_log, "Listening connection has died.\n"); | |
389 | exit(8); | |
390 | } | |
391 | if(OP_STATUS(listenop) != OP_COMPLETE || | |
392 | cp->con == NULL || connection_status(cp->con) != CON_UP) { | |
393 | fprintf(stderr,"Error on listening operation\n"); | |
394 | if (cp->con != NULL && | |
395 | connection_status(cp->con)==CON_STOPPING) { | |
396 | connection_perror(cp->con, | |
397 | "Error on newly started client connection."); | |
398 | sever_connection(cp->con); | |
399 | cp->con = NULL; | |
400 | } else | |
401 | exit(8); | |
402 | } else { | |
403 | /* | |
404 | * Set up the new connection and reply to the client | |
405 | */ | |
406 | cp->state = CL_STARTING; | |
407 | cp->action = CL_ACCEPT; | |
408 | start_replying_to_client(cp->pending_op, cp->con, GDB_ACCEPTED, | |
409 | "", ""); | |
410 | /* | |
411 | * Find a new free connection descriptor. Blow up if | |
412 | * we've used the last one | |
413 | */ | |
414 | for (nextcl=0; nextcl<MAXCLIENTS; nextcl++) | |
415 | if (client[nextcl].state == CL_DEAD) | |
416 | break; | |
417 | ||
418 | if (nextcl == MAXCLIENTS) { | |
419 | fprintf(stderr,"Too many clients, giving up\n"); | |
420 | exit(8); | |
421 | } | |
422 | } | |
423 | /* | |
424 | * Start listening again | |
425 | */ | |
426 | reset_operation(listenop); | |
427 | othersize = sizeof(otherside); | |
428 | ||
429 | start_accepting_client(listencon, listenop, &(client[nextcl].con), | |
430 | (char *)otherside, | |
431 | &othersize, &client_tuple); | |
432 | ||
433 | ||
434 | } |