source: MondoRescue/branches/stable/monitas/client.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: 12.6 KB
Line 
1/* client.c
2
3CLIENT
4
5
6FIXME
7- fork child process ('daemon', almost) to interact with server
8- parent process will interact with keyboard ('logout'=quit)
9- if trigger_backup msg arrives then act accordingly
10- remember, the client is a 'server' from the minute it logs in
11 until the minute it logs out of the server: the client listens
12 on a port between 8800 and 8899, waiting for incoming msgs
13 from server; all the forked process has to do is take over the
14 task of watching that port and act on the incoming data
15- when trigger_backup is received then send file along connection
16
1705/19
18- added tmsg_to_string()
19- forked port-watcher to receive triggers from server in bkgd
20
2105/11
22- clarified structures & their names
23- improved login/logout OK/fail feedback
24
2505/08
26- did some housecleaning
27- added comments; removed strcpy()'s
28- replaced silly exit()'s with return()'s
29
30*/
31
32
33#include "structs.h"
34
35
36
37/* global vars */
38
39
40
41/* prototypes */
42
43int find_and_bind_free_server_port(struct sockaddr_in*, int*);
44long increment_magic_number(void);
45int login_to_server(char*,char*);
46int logout_of_server(char*);
47int send_msg_to_server(struct s_client2server_msg_record*, char*);
48int send_ping_to_server(char*,char*);
49char *tmsg_to_string(t_msg);
50void watch_port_for_triggers_from_server(struct sockaddr_in *, int, int);
51
52/* subroutines */
53
54int find_and_bind_free_server_port(struct sockaddr_in *sin, int *p_s)
55/*
56Purpose:Find a free port on the server. Bind to it, so that
57 whichever subroutine called me can then send data
58 to the server.
59Params: sin - server's IP address in a structure
60 p_s - [return] file descriptor of port binding
61Return: result (0=success, nonzero=failure)
62*/
63{
64 int server_port;
65
66 for(server_port = 8700; server_port < 8710; server_port++)
67 {
68 sin->sin_port = htons(server_port);
69 if ((*p_s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
70 {
71 perror("socket");
72 return(1);
73 }
74 if (connect(*p_s, (struct sockaddr*)sin, sizeof(struct sockaddr_in)) < 0)
75 {
76 printf("Not connecting at %d\r", server_port);
77 continue;
78 }
79 return(server_port);
80 }
81 fprintf(stderr, "Cannot find free server port\nIs server actually running?\n");
82 return(1);
83}
84
85
86
87long increment_magic_number()
88/*
89Purpose:Increment the magic number which is attached to
90 each message sent from client to server, to make
91 the packet unique.
92Params: none
93Return: magic number
94*/
95{
96 static unsigned long magic=1;
97 magic=(magic % 999999999) + 1;
98 return(magic);
99}
100
101
102
103int login_to_server(char*hostname, char*servername)
104/*
105Purpose:Ask server to log me (client) in.
106Params: hostname - client's hostname (not IP address
107 necessarily but it should resolve to it)
108 servername - server's hostname
109Return: result (-1=failure, N=client's port #)
110NB: The client's port # is chosen at random by
111 send_msg_to_server and returned to me.
112*/
113{
114 struct s_client2server_msg_record orig_rec;
115 int i;
116
117 orig_rec.msg_type = login;
118 strncpy(orig_rec.body, hostname, sizeof(orig_rec.body));
119 for(i=0; i<3; i++)
120 {
121 if (send_msg_to_server(&orig_rec, servername) > 0)
122 { return(orig_rec.port); }
123 printf("Attempt #%d to login failed. Retrying...\n", i+1);
124 }
125 printf("Failed to login\n");
126 return(-1);
127}
128
129
130
131int logout_of_server(char*servername)
132/*
133Purpose:Instruct server to log client out.
134Params: servername - hostname of server
135Return: result (0=success; nonzero=failure)
136*/
137{
138 struct s_client2server_msg_record orig_rec;
139
140 orig_rec.msg_type = logout;
141 strncpy(orig_rec.body, "Bye bye!", sizeof(orig_rec.body));
142 if (send_msg_to_server(&orig_rec, servername) <= 0)
143 { return(1); }
144 else
145 { return(0); }
146}
147
148
149
150// FIXME -
151// Are the threads trampling on one another's st_sClient, st_new_sClient's?
152// I don't see how. After all, there's only one thread... :-)
153int send_msg_to_server(struct s_client2server_msg_record *rec, char *servername)
154/*
155Purpose:Send message to server - a login/logout/ping request
156 or perhaps a request for data to be restored.
157Params: rec - the message to be sent to server
158 servername - the hostname of server
159Return: result (-1=failure, >0=success)
160*/
161{
162 struct hostent *hp;
163 struct sockaddr_in sin;
164 static struct sockaddr_in st_sinClient; /* client port; connected by login; closed by logout */
165 static int st_sClient; /* client connection */
166 static int st_new_sClient;
167 int server_port, s, len;
168 static int st_client_port=0; /* client port; set by login */
169 struct s_server2client_msg_record incoming_rec;
170 struct pollfd ufds;
171 static t_pid st_pid=-1;
172
173/* If logging out then kill the trigger-watcher before trying;
174 otherwise, the trigger-watcher will probably catch the
175 'logout_ok' packet and go nutty on us :-)
176*/
177 if (rec->msg_type == logout)
178 {
179 sprintf(tmp, "kill %d &> /dev/null", st_pid);
180 system(tmp);
181// kill(st_pid);
182 st_pid = -1;
183 }
184
185/* st_sinClient, st_new_sClient and st_sClient contain info
186 relating to the permanent socket which this subroutine
187 opens at login-time so that it can listen for messages
188 from the server. This subroutine doesn't watch the
189 socket all the time (yet) but it leaves it open until
190 logout-time, for neatness' sake.
191*/
192 if ((hp = gethostbyname(servername)) == NULL) { fprintf(stderr, "%s: unknown host\n", servername); return(1); }
193 if (st_client_port > 0 && rec->msg_type == login) { fprintf(stderr, "Already logged in. Why try again?\n"); return(1); }
194 if (st_client_port <= 0 && rec->msg_type != login) { fprintf(stderr, "Don't try to send msgs before logging in.\n"); return(1); }
195 /* open client port if login */
196
197 if (rec->msg_type == login)
198 {
199 printf("Logging in\n");
200 st_client_port = 8800 + rand()%100; // FIXME: retry if I can't use this port
201 memset((void*)&st_sinClient, 0, sizeof(st_sinClient));
202 st_sinClient.sin_family = AF_INET;
203 st_sinClient.sin_addr.s_addr = INADDR_ANY;
204 st_sinClient.sin_port = htons(st_client_port);
205 if ((st_sClient = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) { fprintf(stderr, "Unable to open socket on port #%d\n", st_client_port); return(1); }
206 if (bind(st_sClient, (struct sockaddr*)&st_sinClient, sizeof(st_sinClient)) < 0) { fprintf(stderr, "Cannot bind %d - %s\n", st_client_port, strerror(errno)); return(2); }
207 if (listen(st_sClient, MAX_PENDING) < 0) { fprintf(stderr, "Cannot setup listen (%d) - %s\n", st_client_port, strerror(errno)); return(3); }
208 }
209
210 /* send msg to server */
211 rec->port = st_client_port;
212 memset((void*)&sin, 0, sizeof(sin));
213 sin.sin_family = AF_INET;
214 memcpy((void*)&sin.sin_addr, hp->h_addr, hp->h_length);
215 server_port = find_and_bind_free_server_port(&sin, &s);
216 if (server_port<=0) { fprintf(stderr, "Cannot find free server port\nIs server running?\n"); return(1); }
217 rec->magic = increment_magic_number();
218 send (s, (char*)rec, sizeof(struct s_client2server_msg_record), 0);
219 close(s);
220
221 /* wait for ack/pong/feedback */
222 ufds.fd = st_sClient;
223 ufds.events = POLLIN|POLLPRI;
224 poll(&ufds, 1, 1000);
225 if (!ufds.revents) { fprintf(stderr,"Failed to poll\n"); return(-1); }
226 len = sizeof(st_sinClient);
227 if ((st_new_sClient = accept(st_sClient, (struct sockaddr*)&st_sinClient, &len)) < 0) { fprintf(stderr,"[child] Cannot accept\n"); return(1); }
228 if ((len = recv(st_new_sClient, (char*)&incoming_rec, sizeof(incoming_rec), MSG_DONTWAIT)) <= 0) { fprintf(stderr, "[child] Cannot recv\n"); return(2); }
229
230 printf("Received %s - %s\n", tmsg_to_string(incoming_rec.msg_Type, incoming_rec.body);
231 if (rec->msg_type != login && incoming_rec.msg_type == login_ok) { fprintf(stderr, "WTF? login_ok but I wasn't logging in\n"); }
232 if (rec->msg_type != logout&& incoming_rec.msg_type == logout_ok){ fprintf(stderr, "WTF? logout_ok but I wasn't logging out\n"); }
233// FIXME - should I close() after accepting and recving? Or should I wait until logging out?
234
235 /* close client port if logout */
236 if (rec->msg_type == logout)
237 {
238 close(st_new_sClient);
239 st_new_sClient = st_sClient = st_client_port = 0;
240 }
241
242 if (incoming_rec.msg_type == fail) { return(-1); }
243
244/* fork the process which watches in the background for pings/requests/etc. */
245 if (rec->msg_type == login)
246 {
247 st_pid = fork();
248 switch(st_pid)
249 {
250 case -1:
251 fprintf(stderr, "Failed to fork\n"); exit(1);
252 case 0: /* child */
253 watch_port_for_triggers_from_server(&st_sinClient, st_new_sClient, st_sClient);
254 exit(0);
255 default:/* parent */
256 return(server_port);
257 }
258 }
259}
260
261
262
263int send_ping_to_server(char*msg, char*servername)
264/*
265Purpose:Send a 'ping' to server. Wait for 'pong!'.
266Params: msg - string to send ("Hello world"?)
267 servername - server's hostname
268Return: result (0=success, nonzero=failure)
269*/
270{
271 struct s_client2server_msg_record orig_rec;
272 orig_rec.msg_type = ping;
273 strncpy(orig_rec.body, msg, sizeof(orig_rec.body));
274 if (send_msg_to_server(&orig_rec, servername) <= 0)
275 { return(1); }
276 else
277 { return(0); }
278}
279
280
281
282char *tmsg_to_string(t_msg msg_type)
283/*
284Purpose:Return a string/name of the msg of type msg_type
285Params: msg_type - type of message (enum typedef)
286Return: pointer to static string {name of msg)
287*/
288{
289 static char sz_msg[MAX_STR_LEN];
290 switch(msg_type)
291 {
292 case unused : strcpy(sz_msg, "unused"); break;
293 case login : strcpy(sz_msg, "login"); break;
294 case logout : strcpy(sz_msg, "logout"); break;
295 case login_ok:strcpy(sz_msg, "login_ok"); break;
296 case logout_ok:strcpy(sz_msg, "logout_ok"); break;
297 case ping : strcpy(sz_msg, "ping"); break;
298 case pong : strcpy(sz_msg, "pong"); break;
299 case fail : strcpy(sz_msg, "fail"); break;
300 case trigger_backup: strcpy(sz_msg, "trigger_backup"); break;
301 case trigger_restore: strcpy(sz_msg, "trigger_restore"); break;
302 case begin_stream: strcpy(sz_msg, "begin_stream"); break;
303 case end_stream: strcpy(sz_msg, "end_stream"); break;
304 default: strcpy(sz_msg, "(Fix tmsg_to_string please)"); break;
305 }
306 return(sz_msg);
307}
308
309
310void watch_port_for_triggers_from_server(struct sockaddr_in *sin, int new_s, int s)
311/*
312Purpose:Watch client port for incoming trigger
313Params: sin - record containing info on the port/socket
314 new_s - file descriptor of _open_ client socket
315 s - file descriptor of client socket (not the 'open' side of it)
316NB: I don't really know why new_s and s are what they are or how or why.
317Return: none
318{
319 struct pollfd ufds;
320 int len;
321
322 for(;;)
323 {
324 /* wait for ack/pong/feedback */
325 ufds.fd = s;
326 ufds.events = POLLIN|POLLPRI;
327 poll(&ufds, 1, 1000);
328 if (!ufds.revents) { continue; } /* wait again */
329 len = sizeof(struct sockaddr_in);
330 if ((new_s = accept(s, (struct sockaddr*)sin, &len)) < 0) { fprintf(stderr,"[child] Cannot accept\n"); return(1); }
331 if ((len = recv(new_s, (char*)&incoming_rec, sizeof(incoming_rec), MSG_DONTWAIT)) <= 0) { fprintf(stderr, "[child] Cannot recv\n"); return(2); }
332 printf("Received %s - %s\n", tmsg_to_string(incoming_rec.msg_Type, incoming_rec.body);
333// FIXME - should I close() after accepting and recving?
334 }
335}
336
337
338
339/* main */
340
341int main(int argc, char*argv[])
342/*
343Purpose: main subroutine
344Parameters: none
345Return: result (0=success, nonzero=failure)
346*/
347{
348 int client_port;
349 char hostname[MAX_STR_LEN+1], msg[MAX_STR_LEN+1], servername[MAX_STR_LEN+1];
350 bool done;
351
352 srandom(time(NULL));
353 /* FIXME - add Ctrl-C / sigterm trapping */
354 /* FIXME - run login/logout code as a fork and share the logged in/logged out status flag */
355
356 if (argc == 2)
357 {
358 strncpy(servername, argv[1], MAX_STR_LEN);
359 servername[MAX_STR_LEN]='\0';
360 }
361 else
362 {
363 fprintf(stderr, "client <server addr>\n");
364 exit(1);
365 }
366 gethostname(hostname, sizeof(hostname));
367 printf("Logging onto server as client '%s'\n", hostname);
368 client_port = login_to_server(hostname, servername);
369 if (client_port <= 0) { fprintf(stderr, "Unable to login to server. Aborting\n"); exit(1); }
370 for(done=false; !done; )
371 {
372 printf("MsgToSend: ");
373 fgets(msg, sizeof(msg), stdin);
374 if (!strncmp(msg, "logout", 6)) { done=true; }
375 else
376 {
377 if (send_ping_to_server(msg, servername))
378 { fprintf(stderr, "Error while waiting for response to ping\n"); exit(1); }
379 }
380 }
381 printf("Logging out of server\n");
382 if (logout_of_server(servername))
383 { fprintf(stderr, "Warning - failed to logout of server.\n"); }
384 exit(0);
385}
386/* end main() */
387
388
389
Note: See TracBrowser for help on using the repository browser.