1 | /* client.c
|
---|
2 |
|
---|
3 | CLIENT
|
---|
4 |
|
---|
5 |
|
---|
6 | FIXME
|
---|
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 |
|
---|
17 | 05/19
|
---|
18 | - added tmsg_to_string()
|
---|
19 | - forked port-watcher to receive triggers from server in bkgd
|
---|
20 |
|
---|
21 | 05/11
|
---|
22 | - clarified structures & their names
|
---|
23 | - improved login/logout OK/fail feedback
|
---|
24 |
|
---|
25 | 05/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 |
|
---|
43 | int find_and_bind_free_server_port(struct sockaddr_in*, int*);
|
---|
44 | long increment_magic_number(void);
|
---|
45 | int login_to_server(char*,char*);
|
---|
46 | int logout_of_server(char*);
|
---|
47 | int send_msg_to_server(struct s_client2server_msg_record*, char*);
|
---|
48 | int send_ping_to_server(char*,char*);
|
---|
49 | char *tmsg_to_string(t_msg);
|
---|
50 | void watch_port_for_triggers_from_server(struct sockaddr_in *, int, int);
|
---|
51 |
|
---|
52 | /* subroutines */
|
---|
53 |
|
---|
54 | int find_and_bind_free_server_port(struct sockaddr_in *sin, int *p_s)
|
---|
55 | /*
|
---|
56 | Purpose: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.
|
---|
59 | Params: sin - server's IP address in a structure
|
---|
60 | p_s - [return] file descriptor of port binding
|
---|
61 | Return: 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 |
|
---|
87 | long increment_magic_number()
|
---|
88 | /*
|
---|
89 | Purpose:Increment the magic number which is attached to
|
---|
90 | each message sent from client to server, to make
|
---|
91 | the packet unique.
|
---|
92 | Params: none
|
---|
93 | Return: magic number
|
---|
94 | */
|
---|
95 | {
|
---|
96 | static unsigned long magic=1;
|
---|
97 | magic=(magic % 999999999) + 1;
|
---|
98 | return(magic);
|
---|
99 | }
|
---|
100 |
|
---|
101 |
|
---|
102 |
|
---|
103 | int login_to_server(char*hostname, char*servername)
|
---|
104 | /*
|
---|
105 | Purpose:Ask server to log me (client) in.
|
---|
106 | Params: hostname - client's hostname (not IP address
|
---|
107 | necessarily but it should resolve to it)
|
---|
108 | servername - server's hostname
|
---|
109 | Return: result (-1=failure, N=client's port #)
|
---|
110 | NB: 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 |
|
---|
131 | int logout_of_server(char*servername)
|
---|
132 | /*
|
---|
133 | Purpose:Instruct server to log client out.
|
---|
134 | Params: servername - hostname of server
|
---|
135 | Return: 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... :-)
|
---|
153 | int send_msg_to_server(struct s_client2server_msg_record *rec, char *servername)
|
---|
154 | /*
|
---|
155 | Purpose:Send message to server - a login/logout/ping request
|
---|
156 | or perhaps a request for data to be restored.
|
---|
157 | Params: rec - the message to be sent to server
|
---|
158 | servername - the hostname of server
|
---|
159 | Return: 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 |
|
---|
263 | int send_ping_to_server(char*msg, char*servername)
|
---|
264 | /*
|
---|
265 | Purpose:Send a 'ping' to server. Wait for 'pong!'.
|
---|
266 | Params: msg - string to send ("Hello world"?)
|
---|
267 | servername - server's hostname
|
---|
268 | Return: 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 |
|
---|
282 | char *tmsg_to_string(t_msg msg_type)
|
---|
283 | /*
|
---|
284 | Purpose:Return a string/name of the msg of type msg_type
|
---|
285 | Params: msg_type - type of message (enum typedef)
|
---|
286 | Return: 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 |
|
---|
310 | void watch_port_for_triggers_from_server(struct sockaddr_in *sin, int new_s, int s)
|
---|
311 | /*
|
---|
312 | Purpose:Watch client port for incoming trigger
|
---|
313 | Params: 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)
|
---|
316 | NB: I don't really know why new_s and s are what they are or how or why.
|
---|
317 | Return: 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 |
|
---|
341 | int main(int argc, char*argv[])
|
---|
342 | /*
|
---|
343 | Purpose: main subroutine
|
---|
344 | Parameters: none
|
---|
345 | Return: 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 |
|
---|