source: MondoRescue/branches/stable/monitas/server.c@ 351

Last change on this file since 351 was 351, checked in by bcornec, 18 years ago

Add monitas sources - oldest version

  • Property svn:executable set to *
File size: 18.0 KB
Line 
1/* server.c
2
3
4
5SERVER
6
7
8FIXME
9- when sysadm requests it:-
10 - connect to relevant port of client
11 - send trigger_backup msg
12 - receive file(s)
13 - close client's port
14
1505/20
16- changed 'clientname' to 'clientIP'
17
1805/11
19- clarified structures & their names
20- improved login/logout OK/fail feedback
21
2205/10
23- made main loop multithreaded (replaced multiple forks)
24- thanks to the move from forks to threads, clientlist
25 is now shared between processes automatically
26- replaced printf()'s and fprintf()'s with log_it() function
27 and levels debug/info/warn/error/fatal
28
2905/08
30- got down to some housecleaning
31- added comments; removed strcpy()'s
32- replaced silly exit()'s with return()'s
33
34*/
35
36
37
38
39#include "structs.h"
40
41#define LOG_THESE_AND_HIGHER debug /* debug, info, warn, error, fatal */
42#define NOOF_THREADS 10
43
44
45
46
47/* global vars */
48
49struct s_clientlist g_clientlist; /* FIXME - lock during login/logout, using mutexes */
50
51
52
53
54/* prototypes */
55
56int backup_client(int,char*);
57int find_client_in_clientlist(char *);
58int handle_incoming_message(int, struct sockaddr_in *, struct s_client2server_msg_record *);
59int handle_login_request(int, struct s_client2server_msg_record *, char *);
60int handle_logout_request(int, struct s_client2server_msg_record *, char *);
61int handle_ping_request(int, struct s_client2server_msg_record *, char *);
62void log_it(t_loglevel, char *);
63int restore_client(int,char*);
64int send_msg_to_client(struct s_server2client_msg_record *, char *, int);
65void start_threads_to_watch_ports_for_requests(void);
66void interact_with_sysadm(void);
67char *tmsg_to_string(t_msg);
68void* watch_port_for_requests_from_clients(void*);
69
70
71
72/* subroutines */
73
74int backup_client(int clientno, char*path)
75/*
76Purpose:Backup the path of a specific client. Receive
77 the archives. Store them locally (on me, the server).
78Params: clientno - client# in g_clientlist[]
79 path - client's path to be backed up
80Return: result (0=success, nonzero=failure)
81*/
82{
83 struct s_server2client_msg_record rec_to_client;
84 int i, res=0;
85 char tmp[MAX_STR_LEN];
86
87 sprintf(tmp, "Client# = %d", clientno);
88 log_it(debug, tmp);
89 if (clientno >= g_clientlist.items)
90 { log_it(fatal, "backup_client: clientno>=g_clientlist.items"); }
91 sprintf(tmp, "Backing up %s - path=%s", g_clientlist.el[clientno].ipaddr, path);
92 log_it(info, tmp);
93 rec_to_client.msg_type = trigger_backup;
94 strncpy(rec_to_client.body, path, sizeof(rec_to_client.body));
95 if (send_msg_to_client(&rec_to_client, g_clientlist.el[clientno].ipaddr, g_clientlist.el[clientno].port))
96 { log_it(error, "backup_client - failed to send msg to client"); return(1); }
97
98 if (res)
99 {
100 sprintf(tmp, "Error(s) occurred while backing up %s", g_clientlist.el[clientno].ipaddr);
101 log_it(error, tmp);
102 return(1);
103 }
104 else
105 {
106 sprintf(tmp, "Backed up %s OK", g_clientlist.el[clientno].ipaddr);
107 log_it(error, tmp);
108 return(0);
109 }
110}
111
112
113
114int find_client_in_clientlist(char *clientIP)
115/*
116Purpose:Find a client in the clientlist (list of logged-in
117 clients).
118Params: clientIP - IP address of client
119Return: result (<0=not found, 0+=found in element N)
120*/
121{
122 int i;
123 char tmp[MAX_STR_LEN];
124
125 for(i = 0; i < g_clientlist.items; i++)
126 {
127 if (!strcmp(clientIP, g_clientlist.el[i].ipaddr))
128 { return(i); }
129 sprintf(tmp, "find_client_in_clientlist: Compared %s to clientlist[%d]=%s; failed\n", clientIP, i, g_clientlist.el[i].ipaddr);
130 log_it(debug, tmp);
131 }
132 return(-1);
133}
134
135
136
137int handle_incoming_message(int skt, struct sockaddr_in *sin, struct s_client2server_msg_record *rec)
138/*
139Purpose:Process message which has just arrived from client.
140 A 'message' could be a login/logout request or a ping.
141Params: skt - client's port to respond to
142 sin - client's IP address, in sockaddr_in structure
143 rec - data received from client
144Return: result (0=success, nonzero=failure)
145*/
146{
147 char clientIP[MAX_STR_LEN], *ptr, tmp[MAX_STR_LEN];
148 int res=0;
149
150 // echo_ipaddr_to_screen(&sin->sin_addr);
151 ptr = (unsigned char*)(&sin->sin_addr);
152 sprintf(clientIP, "%d.%d.%d.%d", ptr[0], ptr[1], ptr[2], ptr[3]);
153 sprintf(tmp, "clientIP = %s", clientIP);
154 log_it(debug, tmp);
155 switch(rec->msg_type)
156 {
157 case login:
158 res=handle_login_request(skt, rec, clientIP);
159 break;
160 case ping:
161 res=handle_ping_request(skt, rec, clientIP);
162 break;
163 case logout:
164 res=handle_logout_request(skt, rec, clientIP);
165 break;
166 default:
167 sprintf(tmp, "%s request from %s [%s] (port %d) - how do I handle it?", tmsg_to_string(rec->msg_type), clientIP, rec->body, rec->port);
168 log_it(error, tmp);
169 }
170 return(res);
171}
172
173
174
175int handle_login_request(int skt, struct s_client2server_msg_record *rec_from_client, char *clientIP)
176/*
177Purpose:Handle a login request which has just been received
178 from client.
179Params: skt - client's port to talk to
180 rec_from_client - login rq record received from client
181 clientIP - client's IP address, in string
182Return: result (0=success, nonzero=failure)
183*/
184{
185 struct s_server2client_msg_record rec_to_client;
186 int clientno;
187 char tmp[MAX_STR_LEN];
188
189 sprintf(tmp, "Login request from %s [%s] (port %d)", clientIP, rec_from_client->body, rec_from_client->port);
190 log_it(info, tmp);
191 clientno = find_client_in_clientlist(clientIP);
192 if (clientno>=0)
193 {
194 rec_to_client.msg_type = fail;
195 sprintf(rec_to_client.body, "Sorry, you're already logged in!");
196 sprintf(tmp, "Ignoring login rq from %s: he's already logged in.", clientIP);
197 log_it(error, tmp);
198/* FIXME - ping client (which will have a child watching for incoming
199packets by now - you didn't forget to do that, did you? :)) - to find out
200if client is still running. If it's not then say OK, forget it, I'll kill
201that old connection and log you in anew. If it _is_ then say hey, you're
202already logged in; either you're an idiot or you're a hacker. */
203 }
204 else
205 {
206 rec_to_client.msg_type = login_ok; /* to confirm login */
207 sprintf(rec_to_client.body, "Thanks for logging in.");
208 clientno = g_clientlist.items;
209 strncpy(g_clientlist.el[clientno].hostname_pretty, rec_from_client->body, sizeof(g_clientlist.el[clientno].hostname_pretty));
210 strncpy(g_clientlist.el[clientno].ipaddr, clientIP, sizeof(g_clientlist.el[clientno].ipaddr));
211 g_clientlist.el[clientno].port = rec_from_client->port;
212 g_clientlist.items ++;
213 sprintf(tmp, "Login request from %s ACCEPTED", clientIP);
214 log_it(info, tmp);
215 }
216 send_msg_to_client(&rec_to_client, clientIP, rec_from_client->port);
217 return(0);
218}
219
220
221
222int handle_logout_request(int skt, struct s_client2server_msg_record *rec_from_client, char *clientIP)
223/*
224Purpose:Handle a logout request which has just been received
225 from client.
226Params: skt - client's port to talk to
227 rec_from_client - login rq record received from client
228 clientIP - client's IP address, in string
229Return: result (0=success, nonzero=failure)
230*/
231{
232 struct s_server2client_msg_record rec_to_client;
233 int i, res=0;
234 char tmp[MAX_STR_LEN];
235
236 sprintf(tmp, "Logout request from %s [%s] (port %d)", clientIP, rec_from_client->body, rec_from_client->port);
237 log_it(info, tmp);
238 i = find_client_in_clientlist(clientIP);
239 if (i<0)
240 {
241 sprintf(rec_to_client.body, "Client is not logged in yet. How can I log him out?");
242 log_it(error, rec_to_client.body);
243 rec_to_client.msg_type = fail;
244 res=1;
245 }
246 else
247 {
248 sprintf(rec_to_client.body, "Removed client#%d from login table. Thanks for logging out.", i);
249 for(; i<g_clientlist.items; i++)
250 {
251 memcpy((char*)(&g_clientlist.el[i]), (char*)(&g_clientlist.el[i+1]), sizeof(struct s_registered_client_record));
252 }
253 strncpy(g_clientlist.el[i].hostname_pretty, "WTF? Someone teach Hugo to handle pointers properly, please!", sizeof(g_clientlist.el[i].hostname_pretty));
254 g_clientlist.items--;
255 rec_to_client.msg_type = logout_ok; /* to confirm logout */
256 sprintf(tmp, "Logout request from %s ACCEPTED", clientIP);
257 log_it(info, tmp);
258 }
259 send_msg_to_client(&rec_to_client, clientIP, rec_from_client->port);
260 return(res);
261}
262
263
264
265int handle_ping_request(int skt, struct s_client2server_msg_record *rec_from_client, char *clientIP)
266/*
267Purpose:Handle a ping request which has just been received
268 from client.
269Params: skt - client's port to talk to
270 rec_from_client - login rq record received from client
271 clientIP - client's IP address, in string
272Return: result (0=success, nonzero=failure)
273*/
274{
275 struct s_server2client_msg_record rec_to_client;
276 int i;
277 char tmp[MAX_STR_LEN];
278
279 sprintf(tmp, "ping request from port %d --- %s", rec_from_client->port, rec_from_client->body);
280 log_it(info, tmp);
281 i = find_client_in_clientlist(clientIP);
282 if (i < 0)
283 {
284 sprintf(tmp, "Hey, %s isn't logged in. I'm not going to pong him.", clientIP);
285 log_it(error, tmp);
286 }
287 else
288 {
289 rec_to_client.msg_type = pong; /* reply to ping */
290 sprintf(rec_to_client.body, "Hey, I'm replying to client#%d's ping. Pong! (re: %s", i, rec_from_client->body);
291 send_msg_to_client(&rec_to_client, clientIP, rec_from_client->port);
292 log_it(debug, rec_to_client.body);
293 }
294 return(0);
295}
296
297
298
299void interact_with_sysadm()
300/*
301Purpose:Tell sysadm about users logged in, etc. in
302 response to keypresses (1=users, ENTER=exit)
303Params: none
304Return: none
305NB: Called by main()
306*/
307{
308 char tmp[MAX_STR_LEN];
309 int i;
310 bool done;
311
312 for(done=false; !done; )
313 {
314 printf("Type 1 to list clients\n");
315 printf("Type 2 to backup a client\n");
316 printf("Type 3 to restore a client\n");
317 printf("Type ENTER to quit.\n");
318 fgets(tmp, sizeof(tmp), stdin);
319 if (!strcmp(tmp, "1\n"))
320 {
321 printf("g_clientlist.items is %d\n", g_clientlist.items);
322 printf("List of %d clients:-\n", g_clientlist.items);
323 printf("\tClient# Port# IP address Client name\n");
324 for(i=0; i<g_clientlist.items; i++)
325 {
326 printf("\t%d\t%4d %-16s %-20s \n", i, g_clientlist.el[i].port, g_clientlist.el[i].ipaddr, g_clientlist.el[i].hostname_pretty);
327 }
328 printf("<End of list>\n\n");
329 }
330 else if (!strcmp(tmp, "2\n"))
331 {
332 if (g_clientlist.items!=1) { printf("Forget it\n"); }
333 else
334 {
335 backup_client(0, "/usr/local");
336 }
337 }
338 else if (!strcmp(tmp, "3\n"))
339 {
340 if (g_clientlist.items!=1) { printf("Forget it\n"); }
341 else
342 {
343 restore_client(0, "/usr/local");
344 }
345 }
346 else if (!strcmp(tmp, "\n"))
347 {
348 if (g_clientlist.items > 0)
349 {
350 printf("There are %d users logged in. Are you sure you want to quit? ", g_clientlist.items);
351 fgets(tmp, sizeof(tmp), stdin);
352 if (tmp[0]=='Y' || tmp[0]=='y')
353 { done=true; }
354 }
355 else
356 { done=true; }
357 if (done)
358 { log_it(info, "OK, you've chosen to shutdown server."); }
359 else
360 { log_it(info, "Shutdown canceled."); }
361 }
362 }
363}
364
365
366
367void log_it(t_loglevel level, char *sz_message)
368{
369 char sz_outstr[MAX_STR_LEN], sz_level[MAX_STR_LEN], sz_time[MAX_STR_LEN];
370 time_t time_rec;
371
372 time(&time_rec);
373 switch(level)
374 {
375 case debug: strcpy(sz_level, "DEBUG"); break;
376 case info: strcpy(sz_level, "INFO "); break;
377 case warn: strcpy(sz_level, "WARN "); break;
378 case error: strcpy(sz_level, "ERROR"); break;
379 case fatal: strcpy(sz_level, "FATAL"); break;
380 default: strcpy(sz_level, "UNKWN"); break;
381 }
382 sprintf(sz_time, ctime(&time_rec));
383 sz_time[strlen(sz_time)-6] = '\0';
384 sprintf(sz_outstr, "%s %s: %s", sz_time+11, sz_level, sz_message);
385 if ((int)level >= LOG_THESE_AND_HIGHER)
386 {
387 fprintf(stderr, "%s\n",sz_outstr);
388 }
389 if (level==fatal) { fprintf(stderr,"Aborting now."); exit(1); }
390}
391
392
393
394
395int restore_client(int clientno, char*path)
396{
397}
398
399
400int send_msg_to_client(struct s_server2client_msg_record *rec, char *clientIP, int port)
401/*
402Purpose:Send a message from server to client.
403 A 'message' could be a response to a login/logout/ping
404 request or perhaps a 'trigger'. (A trigger is a message
405 from server to client intended to initiate a backup
406 or similar activity.)
407Params: rec - record containing the data to be sent to client
408 clientIP - the xxx.yyy.zzz.aaa IP address of client
409 port - the client's port to send data to
410Return: result (0=success, nonzero=failure)
411*/
412{
413 struct hostent *hp;
414 struct sockaddr_in sin;
415 int s;
416 char tmp[MAX_STR_LEN];
417
418 if ((hp = gethostbyname(clientIP)) == NULL)
419 {
420 sprintf(tmp, "send_msg_to_client: %s: unknown host\n", clientIP);
421 log_it(error, tmp);
422 return(1);
423 }
424 memset((void*)&sin, 0, sizeof(sin));
425 memcpy((void*)&sin.sin_addr, hp->h_addr, hp->h_length);
426 sin.sin_family = AF_INET;
427 sin.sin_addr.s_addr = INADDR_ANY;
428 sin.sin_port = htons(port);
429 if ((s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
430 { perror("socket"); log_it(error, "send_msg_to_client: SOCKET error"); return(1); }
431 if (connect(s, (struct sockaddr*)&sin, sizeof(struct sockaddr_in)) < 0)
432 { sprintf(tmp, "Failed to connect to client %s on port %d", clientIP, port); log_it(error, tmp); return(1); }
433 send(s, (char*)rec, sizeof(struct s_server2client_msg_record), 0);
434 close(s);
435 sprintf(tmp, "Sent %s msg to %s (port %d)", tmsg_to_string(rec->msg_type), clientIP, port);
436 log_it(debug, tmp);
437 return(0);
438}
439
440
441
442void start_threads_to_watch_ports_for_requests()
443/*
444Purpose:Start the threads that watch the 'incoming' ports
445 for messages from clients.
446Params: none
447Return: none
448NB: Called by main()
449*/
450{
451 int i, port, res;
452 pthread_t threadinfo[NOOF_THREADS];
453 char tmp[MAX_STR_LEN];
454
455 g_clientlist.items = 0;
456 for(i=0; i<NOOF_THREADS; i++)
457 {
458 port = 8700+i;
459 sprintf(tmp,"%d", port);
460 res = pthread_create(&(threadinfo[i]), NULL, watch_port_for_requests_from_clients, (void*)tmp);
461 if (res != 0)
462 {
463 perror("Thread creation failed");
464 }
465 usleep(80000);
466 }
467 sprintf(tmp, "Threads are now running.");
468 log_it(info, tmp);
469}
470
471
472
473char *tmsg_to_string(t_msg msg_type)
474/*
475Purpose:Return a string/name of the msg of type msg_type
476Params: msg_type - type of message (enum typedef)
477Return: pointer to static string {name of msg)
478*/
479{
480 static char sz_msg[MAX_STR_LEN];
481 switch(msg_type)
482 {
483 case unused : strcpy(sz_msg, "unused"); break;
484 case login : strcpy(sz_msg, "login"); break;
485 case logout : strcpy(sz_msg, "logout"); break;
486 case login_ok:strcpy(sz_msg, "login_ok"); break;
487 case logout_ok:strcpy(sz_msg, "logout_ok"); break;
488 case ping : strcpy(sz_msg, "ping"); break;
489 case pong : strcpy(sz_msg, "pong"); break;
490 case fail : strcpy(sz_msg, "fail"); break;
491 case trigger_backup: strcpy(sz_msg, "trigger_backup"); break;
492 case trigger_restore: strcpy(sz_msg, "trigger_restore"); break;
493 case begin_stream: strcpy(sz_msg, "begin_stream"); break;
494 case end_stream: strcpy(sz_msg, "end_stream"); break;
495 default: strcpy(sz_msg, "(Fix tmsg_to_string please)"); break;
496 }
497 return(sz_msg);
498}
499
500
501
502void* watch_port_for_requests_from_clients(void*sz_watchport)
503/*
504Purpose:Watch a port for incoming messages from clients.
505 A 'message' could be a request to login/logout or
506 a ping, or perhaps a request to backup/restore data.
507Params: sz_watchport - the port to watch for incoming messages
508 from clients
509Return: result (0=success, nonzero=failure)
510NB: Function will return nonzero if error occurs during
511 setup but will otherwise run forever, or until killed.
512*/
513{
514 int watch_port;
515 struct sockaddr_in sin;
516 char buf[MAX_STR_LEN+1], tmp[MAX_STR_LEN];
517 int len, s, new_s;
518 struct s_client2server_msg_record rec;
519
520 watch_port = atoi((char*)sz_watchport);
521 sprintf(tmp, "watch_port_for_requests_from_clients(%d) - starting", watch_port);
522 log_it(debug, tmp);
523 memset((void*)&sin, 0, sizeof(sin));
524 sin.sin_family = AF_INET;
525 sin.sin_addr.s_addr = INADDR_ANY;
526 sin.sin_port = htons(watch_port);
527 if ((s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
528 {
529 sprintf(tmp, "Unable to open socket on port #%d", watch_port);
530 log_it(error, tmp);
531 return((void*)-1);
532 }
533 if (bind(s, (struct sockaddr*)&sin, sizeof(sin)) < 0)
534 {
535 sprintf(tmp, "Cannot bind %d - %s\n", watch_port, strerror(errno));
536 log_it(error, tmp);
537 return((void*)-1);
538 }
539 if (listen(s, MAX_PENDING) < 0)
540 {
541 sprintf(tmp, "Cannot setup listen (%d) - %s\n", watch_port, strerror(errno));
542 log_it(error, tmp);
543 return((void*)-1);
544 }
545 /* service incoming connections */
546 sprintf(tmp, "Opened socket on port #%d OK", watch_port);
547 log_it(info, tmp);
548 while(true)
549 {
550 len = sizeof(sin);
551 if ((new_s = accept(s, (struct sockaddr *)&sin, (unsigned int*)&len)) < 0)
552 {
553 sleep(1);
554 continue;
555 }
556 while ((len = recv(new_s, buf, sizeof(buf), 0)) > 0)
557 {
558 if (len > MAX_STR_LEN) { len = MAX_STR_LEN; }
559 buf[len] = '\0';
560 memcpy((char*)&rec, buf, sizeof(rec));
561 handle_incoming_message(new_s, &sin, &rec);
562 }
563 close(new_s);
564 }
565 return(NULL);
566}
567
568
569
570int main(int argc, char*argv[])
571/*
572Purpose: main subroutine
573Parameters: none
574Return: result (0=success, nonzero=failure)
575*/
576{
577 char tmp[MAX_STR_LEN];
578
579/* FIXME - add Ctrl-C / sigterm trapping */
580
581 start_threads_to_watch_ports_for_requests();
582 interact_with_sysadm(); /* report on users logged in, etc.; return after a while... */
583 if (g_clientlist.items != 0)
584 { fprintf(stderr, "Ruh-roh! Some users are still logged in. Oh well...\n"); }
585/* FIXME - force clients to log off */
586/* FIXME - send signal to threads, telling them to terminate */
587 sprintf(tmp, "Done. Server is exiting now.");
588 log_it(info, tmp);
589 return(0);
590}
591/* end main() */
592
593
Note: See TracBrowser for help on using the repository browser.