source: branches/stable/monitas/server.c @ 352

Last change on this file since 352 was 352, checked in by bcornec, 13 years ago

monitas v0.1a

  • Property svn:executable set to *
File size: 32.4 KB
Line 
1/* server.c
2
3
4
506/19
6- fixed bugs in mondoarchive compare code
7
806/16
9- every 15 seconds, update /var/spool/monitas/server-status.txt file
10- added handle_progress_rpt()
11- modify restore_path() to allow user to restore to another path
12
1306/14
14- added "2> /dev/null" to call to find /var/spool/monitas
15- improved backup_client(), compare_client(), restore_client()
16- pipe logs to logfile, not stdout
17
1806/11
19- moved register_pid(), set_signals(), termination_in_progress() to common.c
20- commented code a bit
21- improved backup_client(), compare_client(), restore_client()
22
2306/10
24- improved backup_client()
25- discover whether mondoarchive returned error (ask client)
26  and act accordingly (i.e. drop archive)
27
2806/09
29- added /var/spool/monitas/input.dev FIFO to
30  let external programs instruct me to backup
31  or restore a given computer
32- added signal trapping
33
3406/07
35- rename transmit_file... and move
36  to common.c, as transmit_file_to_socket()
37- rename receive_file... and move to
38  common.c, as receive_file_from_socket()
39
4005/27
41- save bkps to /var/spool/monitas/123.456.789.012/[time]
42
4305/23
44- added elementary saving/storing of incoming data
45  from client backing itself up
46
4705/21
48- added backup_client, compare_client, restore_client
49  templates for triggers
50 
5105/11
52- clarified structures & their names
53- improved login/logout OK/fail feedback
54
5505/10
56- made main loop multithreaded (replaced multiple forks)
57- thanks to the move from forks to threads, clientlist
58  is now shared between processes automatically
59- replaced printf()'s and fprintf()'s with log_it() function
60  and levels debug/info/warn/error/fatal
61
6205/08
63- got down to some housecleaning
64- added comments; removed strcpy()'s
65- replaced silly exit()'s with return()'s
66
67*/
68
69
70
71
72/*-----------------------------------------------------------*/
73
74
75
76#include "structs.h"
77//#define LOG_THESE_AND_HIGHER debug
78
79
80
81#define NOOF_THREADS 10
82#define LOGFILE "/var/log/monitas-server.log"
83#define log_it(x,y) { log_it_SUB(g_logfile,x,y); }
84
85
86/* externs */
87
88extern bool does_file_exist(char*);
89extern char *call_program_and_get_last_line_of_output(char*);
90extern int call_program_and_log_output(char*);
91extern int create_and_watch_fifo_for_commands(char*);
92extern bool does_file_exist(char*);
93extern void log_it_SUB(char*, t_loglevel, char *);
94extern int make_hole_for_file (char *);
95extern int read_block_from_fd(int socket_fd, char*buf, int len_to_read);
96extern int receive_file_from_socket(FILE*, int);
97extern void register_pid(pid_t, char*);
98extern void set_signals(bool);
99extern void termination_in_progress(int);
100extern char *tmsg_to_string(t_msg);
101extern int transmit_file_to_socket(FILE*,int);
102
103
104
105/* global vars */
106
107struct s_clientlist g_clientlist; /* FIXME - lock during login/logout, using mutexes */
108char g_command_fifo[MAX_STR_LEN+1]; // Device which server will monitor for incoming commands
109pthread_t g_threadinfo[NOOF_THREADS]; // One thread per port, to watch for requests from clients
110char g_logfile[MAX_STR_LEN+1] = "/var/log/monitas-server.log";
111char g_server_status_file[MAX_STR_LEN+1];
112
113
114
115/* prototypes */
116
117int backup_client(char*, int, char*);
118int compare_client(char*, int, char*);
119int find_client_in_clientlist(char *);
120int forcibly_logout_all_clients(void);
121int forcibly_logout_client(int);
122void* generate_server_status_file_regularly(void*);
123int handle_incoming_message(int, struct sockaddr_in *, struct s_client2server_msg_record *);
124int handle_login_request(int, struct s_client2server_msg_record *, char *);
125int handle_logout_request(int, struct s_client2server_msg_record *, char *);
126int handle_ping_request(int, struct s_client2server_msg_record *, char *);
127int handle_progress_rpt(int, struct s_client2server_msg_record *, char *);
128int handle_user_request(int, struct s_client2server_msg_record *, char *);
129int process_incoming_command(char*);
130int read_block_from_fd(int, char*, int);
131int restore_client(char*, int, char*, char*);
132int send_msg_to_client(struct s_server2client_msg_record *, char *, int, int*);
133void start_threads_to_watch_ports_for_requests(void);
134void terminate_daemon(int);
135char *tmsg_to_string(t_msg);
136void* watch_port_for_requests_from_clients(void*);
137
138
139
140/*-----------------------------------------------------------*/
141
142
143
144int backup_client(char*ipaddr, int port, char*clientpath)
145/*
146Purpose:Backup the path of a specific client. Receive
147the archives. Store them locally (on me, the server).
148Params: clientno - client# in g_clientlist[]
149        clientpath - client's path to be backed up
150Return: result (0=success, nonzero=failure)
151*/
152{
153  struct s_server2client_msg_record rec_to_client;
154  int res=0, socket_fd, noof_archives, i, len;
155  char tmp[MAX_STR_LEN+1], outfile[MAX_STR_LEN+1];
156  FILE*fout;
157
158  sprintf(tmp, "%s - backup of %s commencing", ipaddr, clientpath);
159  log_it(info, tmp);
160  sprintf(outfile, "/var/spool/monitas/%s/%s.dat", ipaddr, call_program_and_get_last_line_of_output("date +%s"));
161  if (does_file_exist(outfile)) { log_it(error, "Backup storage location exists already. That should be impossible."); return(1); }
162  if (make_hole_for_file(outfile))
163    { res++; log_it(error, "Cannot write archive to spool dir"); }
164  else if (!(fout=fopen(outfile, "w")))
165    { res++; log_it(fatal, "Failed to openout temp data file"); }
166  else
167    {
168      sprintf(tmp, "Backing up %s - archive=%s", ipaddr, outfile);
169      log_it(debug, tmp);
170      rec_to_client.msg_type = trigger_backup;
171      strncpy(rec_to_client.body, clientpath, sizeof(rec_to_client.body));
172      if (send_msg_to_client(&rec_to_client, ipaddr, port, &socket_fd))
173        { log_it(error, "backup_client - failed to send msg to client"); return(1); }
174      res += receive_file_from_socket(fout, socket_fd);
175      len=read(socket_fd, (char*)&i, sizeof(i));
176      if (!len) { res++; log_it(error, "Client hasn't told me the result of its call to mondoarchive"); }
177      else if (len!=sizeof(i)) { res++; log_it(error, "Client didn't sent _entire_ result of its call to mondoarchive"); }
178      else if (i) { res++; log_it(error, "Client said, mondoarchive returned an error."); }
179      else { log_it(debug, "Client said, mondoarchive returned OK"); }
180      fclose(fout);
181      close(socket_fd);
182    }
183/* Shuffle older backups off the mortal coil. Leave maximum of 4 backup files in /var/spool/monitas/[ipaddr] */
184  sprintf(tmp, "find /var/spool/monitas/%s -type f 2> /dev/null | grep -n \"\" | tail -n1 | cut -d':' -f1", ipaddr);
185  noof_archives = atoi(call_program_and_get_last_line_of_output(tmp));
186  i = noof_archives - 3;
187  if (i>0)
188    {
189      sprintf(tmp, "rm -f `find /var/spool/monitas/%s -type f | sort | head -n%d`", ipaddr, i);
190      call_program_and_log_output(tmp);
191    }
192/* Return success/failure value */
193  if (res>0)
194    {
195      sprintf(tmp, "%s - error(s) occurred while backing up %s", ipaddr, clientpath);
196      log_it(error, tmp);
197      rec_to_client.msg_type = backup_fail;
198      sprintf(rec_to_client.body, "Failed to backup %s", clientpath);
199      log_it(debug, rec_to_client.body);
200      unlink(outfile);
201    }
202  else
203    {
204      sprintf(tmp, "%s - backed up %s ok", ipaddr, clientpath);
205      log_it(info, tmp);
206      rec_to_client.msg_type = backup_ok;
207      sprintf(rec_to_client.body, "%s - backed up ok", clientpath);
208      log_it(debug, rec_to_client.body);
209    }
210  if (send_msg_to_client(&rec_to_client, ipaddr, port, &socket_fd))
211    {
212      res++;
213      sprintf(tmp, "Unable to notify %s of backup success/failure", ipaddr);
214      log_it(error, tmp);
215      i = find_client_in_clientlist(ipaddr);
216      if (i>=0) { forcibly_logout_client(i); }
217      log_it(info, "I'm assuming the backup was bad because the client cannot be reached.");
218      unlink(outfile);
219    }
220  return(res);
221}
222
223
224
225/*-----------------------------------------------------------*/
226
227
228
229int compare_client(char*ipaddr, int port, char*clientpath)
230/*
231Purpose:Compare the path of a specific client. Transmit
232the archives from my (the server's) local storage loc.
233Params: clientno - client# in g_clientlist[]
234        clientpath - client's path to be compared
235Return: result (0=success, nonzero=failure)
236*/
237{
238  struct s_server2client_msg_record rec_to_client;
239  int res=0, socket_fd, len, i;
240  char tmp[MAX_STR_LEN+1], infile[MAX_STR_LEN+1];
241  FILE*fin;
242
243  sprintf(tmp, "%s - comparison of %s commencing", ipaddr, clientpath);
244  log_it(info, tmp);
245// FIXME - don't assume the latest backup contains the files we want ;)
246  sprintf(tmp, "find /var/spool/monitas/%s -type f | sort | tail -n1", ipaddr);
247  strcpy(infile, call_program_and_get_last_line_of_output(tmp));
248  sprintf(tmp, "Comparing to data file '%s'", infile);
249  log_it(debug, tmp);
250  if (!does_file_exist(infile)) { log_it(error, "Backup not found. That should be impossible."); return(1); }
251  sprintf(tmp, "Comparing %s - archive=%s", ipaddr, infile);
252  log_it(debug, tmp);
253  rec_to_client.msg_type = trigger_compare;
254  strncpy(rec_to_client.body, clientpath, sizeof(rec_to_client.body));
255  if (send_msg_to_client(&rec_to_client, ipaddr, port, &socket_fd))
256    { log_it(error, "compare_client - failed to send msg to client"); return(1); }
257  if (!(fin=fopen(infile, "r")))
258    { log_it(fatal, "Failed to openin temp data file"); }
259  res += transmit_file_to_socket(fin, socket_fd);
260  len=read(socket_fd, (char*)&i, sizeof(i));
261  if (!len) { res++; log_it(error, "Client hasn't told me the result of its call to mondoarchive"); }
262  else if (len!=sizeof(i)) { res++; log_it(error, "Client didn't sent _entire_ result of its call to mondoarchive"); }
263  else if (i) { res++; log_it(error, "Client said, mondoarchive returned an error."); }
264  else { log_it(debug, "Client said, mondoarchive returned OK"); }
265  fclose(fin);
266  close(socket_fd);
267  if (res>0)
268    {
269      sprintf(tmp, "%s - error(s) occurred while comparing %s", ipaddr, clientpath);
270      log_it(error, tmp);
271      rec_to_client.msg_type = compare_fail;
272      sprintf(rec_to_client.body, "Failed to compare %s", clientpath);
273      log_it(debug, rec_to_client.body);
274    }
275  else
276    {
277      sprintf(tmp, "%s - compared %s ok", ipaddr, clientpath);
278      log_it(info, tmp);
279      rec_to_client.msg_type = compare_ok;
280      sprintf(rec_to_client.body, "%s - compared ok", clientpath);
281      log_it(debug, rec_to_client.body);
282    }
283  if (send_msg_to_client(&rec_to_client, ipaddr, port, &socket_fd))
284    {
285      sprintf(tmp, "Unable to notify %s of compare success/failure", ipaddr);
286      log_it(error, tmp);
287      i = find_client_in_clientlist(ipaddr);
288      if (i>=0) { forcibly_logout_client(i); }
289    }
290  return(res);
291}
292
293
294
295/*-----------------------------------------------------------*/
296
297
298
299
300int find_client_in_clientlist(char *clientIP)
301/*
302Purpose:Find a client in the clientlist (list of logged-in
303    clients).
304Params: clientIP - IP address of client
305Return: result (<0=not found, 0+=found in element N)
306*/
307{
308  int i;
309  char tmp[MAX_STR_LEN+1];
310
311  for(i = 0; i < g_clientlist.items; i++)
312    {
313      if (!strcmp(clientIP, g_clientlist.el[i].ipaddr))
314    { return(i); }
315      sprintf(tmp, "find_client_in_clientlist: Compared %s to clientlist[%d]=%s; failed\n", clientIP, i, g_clientlist.el[i].ipaddr);
316      log_it(debug, tmp);
317    }
318  return(-1);
319}
320
321
322
323/*-----------------------------------------------------------*/
324
325
326
327int forcibly_logout_all_clients()
328/*
329Purpose: Tell all clients to disconnect, right now.
330Params:  None
331Returns: 0=success
332*/
333{
334// FIXME - lock g_clientlist for duration of this function
335  while(g_clientlist.items>0)
336    {
337      forcibly_logout_client(0);
338    }
339  return(0);
340}
341
342
343
344/*-----------------------------------------------------------*/
345
346
347
348int forcibly_logout_client(int clientno)
349/*
350Purpose: Logout specific client(#) by force.
351Params:  Client# in g_clientlist[] array.
352Returns: 0=success, nonzero=failure to get other end to hear me;)
353NB:      The client was definitely removed from our login table.
354         If the client got the message, return 0; else, nonzero.
355*/
356{
357  struct s_server2client_msg_record rec_to_client;
358  char tmp[MAX_STR_LEN+1];
359  int res=0;
360
361  sprintf(tmp, "Forcibly logging %s out", g_clientlist.el[clientno].ipaddr);
362  log_it(info, tmp);
363  rec_to_client.msg_type = logout_ok; /* to confirm logout */
364  strcpy(rec_to_client.body, "Server is shutting down. You are forced to logout");
365  res=send_msg_to_client(&rec_to_client, g_clientlist.el[clientno].ipaddr, g_clientlist.el[clientno].port, NULL);
366  if (--g_clientlist.items > 0)
367    {
368      sprintf(tmp, "Moving clientlist[%d] to clientlist[%d]", clientno, g_clientlist.items);
369      log_it(debug, tmp);
370      sprintf(tmp, "Was ipaddr=%s; now is ipaddr=", g_clientlist.el[clientno].ipaddr);
371      memcpy((void*)&g_clientlist.el[clientno], (void*)&g_clientlist.el[g_clientlist.items], sizeof(struct s_registered_client_record));
372      strcat(tmp, g_clientlist.el[clientno].ipaddr);
373      log_it(debug, tmp);
374    }
375  return(res);
376}
377
378
379
380/*-----------------------------------------------------------*/
381
382
383void* generate_server_status_file_regularly(void*inp)
384{
385  int i;
386  FILE*fout;
387
388  strncpy(g_server_status_file, (char*)inp, MAX_STR_LEN);
389  for(;;)
390    {
391      if ((fout = fopen(g_server_status_file, "w")))
392        {
393// FIXME - lock g_clientlist
394          for(i=0; i<g_clientlist.items; i++)
395            {
396              fprintf(fout, "%s [%s] : %s\n", g_clientlist.el[i].ipaddr, g_clientlist.el[i].hostname_pretty, g_clientlist.el[i].last_progress_rpt);
397            }
398          fclose(fout);
399        }
400      sleep(1);
401    }
402  exit(0);
403}
404
405
406
407/*-----------------------------------------------------------*/
408
409
410
411int handle_incoming_message(int skt, struct sockaddr_in *sin, struct s_client2server_msg_record *rec)
412/*
413Purpose:Process message which has just arrived from client.
414    A 'message' could be a login/logout request or a ping.
415Params: skt - client's port to respond to
416    sin - client's IP address, in sockaddr_in structure
417    rec - data received from client
418Return: result (0=success, nonzero=failure)
419*/
420{
421  char clientIP[MAX_STR_LEN+1], tmp[MAX_STR_LEN+1];
422  unsigned char *ptr;
423  int res=0;
424
425  //  echo_ipaddr_to_screen(&sin->sin_addr);
426  ptr = (unsigned char*)(&sin->sin_addr);
427  sprintf(clientIP, "%d.%d.%d.%d", ptr[0], ptr[1], ptr[2], ptr[3]);
428  sprintf(tmp, "clientIP = %s", clientIP);
429  log_it(debug, tmp);
430  sprintf(tmp, "%s message from %s [%s] (port %d)", tmsg_to_string(rec->msg_type), clientIP, rec->body, rec->port);
431  log_it(debug, tmp);
432  switch(rec->msg_type)
433    {
434    case login:
435      res=handle_login_request(skt, rec, clientIP);
436      break;
437    case ping:
438      res=handle_ping_request(skt, rec, clientIP);
439      break;
440    case progress_rpt:
441      res=handle_progress_rpt(skt, rec, clientIP);
442      break;
443    case logout:
444      res=handle_logout_request(skt, rec, clientIP);
445      break;
446    case user_req:
447      res=handle_user_request(skt, rec, clientIP);
448      break;
449    default:
450      log_it(error, "...How do I handle it?");
451    }
452  return(res);
453}
454
455
456
457/*-----------------------------------------------------------*/
458
459
460
461int handle_login_request(int skt, struct s_client2server_msg_record *rec_from_client, char *clientIP)
462/*
463Purpose:Handle a login request which has just been received
464    from client.
465Params: skt - client's port to talk to
466    rec_from_client - login rq record received from client
467    clientIP - client's IP address, in string
468Return: result (0=success, nonzero=failure)
469*/
470{
471  struct s_server2client_msg_record rec_to_client;
472  int clientno;
473  char tmp[MAX_STR_LEN+1];
474
475//FIXME - lock g_clientlist[]
476  clientno = find_client_in_clientlist(clientIP);
477  if (clientno>=0)
478    {
479      rec_to_client.msg_type = login_fail;
480      sprintf(rec_to_client.body, "Sorry, you're already logged in!");
481      sprintf(tmp, "Ignoring login rq from %s: he's already logged in.", clientIP);
482      log_it(error, tmp);
483/* FIXME - ping client (which will have a child watching for incoming
484packets by now - you didn't forget to do that, did you? :)) - to find out
485if client is still running. If it's not then say OK, forget it, I'll kill
486that old connection and log you in anew. If it _is_ then say hey, you're
487already logged in; either you're an idiot or you're a hacker. */
488    }
489  else
490    {
491      rec_to_client.msg_type = login_ok; /* to confirm login */
492      sprintf(rec_to_client.body, "Thanks for logging in.");
493      clientno = g_clientlist.items;
494      strncpy(g_clientlist.el[clientno].hostname_pretty, rec_from_client->body, sizeof(g_clientlist.el[clientno].hostname_pretty));
495      strncpy(g_clientlist.el[clientno].ipaddr, clientIP, sizeof(g_clientlist.el[clientno].ipaddr));
496      g_clientlist.el[clientno].port = rec_from_client->port;
497      g_clientlist.el[clientno].busy = false;
498      g_clientlist.items ++;
499      sprintf(tmp, "Login request from %s ACCEPTED", clientIP);
500      log_it(info, tmp);
501      strcpy(g_clientlist.el[clientno].last_progress_rpt, "Logged in");
502    }
503  send_msg_to_client(&rec_to_client, clientIP, rec_from_client->port, NULL);
504  return(0);
505}
506
507
508
509/*-----------------------------------------------------------*/
510
511
512
513int handle_logout_request(int skt, struct s_client2server_msg_record *rec_from_client, char *clientIP)
514/*
515Purpose:Handle a logout request which has just been received
516    from client.
517Params: skt - client's port to talk to
518    rec_from_client - logout rq record received from client
519    clientIP - client's IP address, in string
520Return: result (0=success, nonzero=failure)
521*/
522{
523  struct s_server2client_msg_record rec_to_client;
524  int i, res=0;
525  char tmp[MAX_STR_LEN+1];
526
527  i = find_client_in_clientlist(clientIP);
528  if (i<0)
529    {
530      sprintf(rec_to_client.body, "Client is not logged in yet. How can I log him out?");
531      log_it(error, rec_to_client.body);
532      rec_to_client.msg_type = logout_fail;
533      res=1;
534    }
535  else if (g_clientlist.el[i].busy)
536    {
537      sprintf(rec_to_client.body, "Client is working. I shouldn't log him out.");
538      log_it(error, rec_to_client.body);
539      rec_to_client.msg_type = logout_fail;
540      res=1;
541    }
542  else
543    {
544      sprintf(rec_to_client.body, "Removed client#%d from login table. Thanks for logging out.", i);
545      for(; i<g_clientlist.items; i++)
546        {
547      memcpy((char*)(&g_clientlist.el[i]), (char*)(&g_clientlist.el[i+1]), sizeof(struct s_registered_client_record));
548    }
549      strncpy(g_clientlist.el[i].hostname_pretty, "WTF? Someone teach Hugo to handle pointers properly, please!", sizeof(g_clientlist.el[i].hostname_pretty));
550      g_clientlist.items--;
551      rec_to_client.msg_type = logout_ok; /* to confirm logout */
552      sprintf(tmp, "Logout request from %s ACCEPTED", clientIP);
553      log_it(info, tmp);
554    }
555  send_msg_to_client(&rec_to_client, clientIP, rec_from_client->port, NULL);
556  return(res);
557}
558
559
560
561/*-----------------------------------------------------------*/
562
563
564
565int handle_ping_request(int skt, struct s_client2server_msg_record *rec_from_client, char *clientIP)
566/*
567Purpose:Handle a ping request which has just been received
568    from client.
569Params: skt - client's port to talk to
570    rec_from_client - ping record received from client
571    clientIP - client's IP address, in string
572Return: result (0=success, nonzero=failure)
573*/
574{
575  struct s_server2client_msg_record rec_to_client;
576  int i;
577  char tmp[MAX_STR_LEN+1];
578
579  i = find_client_in_clientlist(clientIP);
580  if (i < 0)
581    {
582      sprintf(tmp, "Hey, %s isn't logged in. I'm not going to pong him.", clientIP);
583      log_it(error, tmp);
584    }
585  else
586    {
587      rec_to_client.msg_type = pong; /* reply to ping */
588      sprintf(rec_to_client.body, "Hey, I'm replying to client#%d's ping. Pong! (re: %s", i, rec_from_client->body);
589      send_msg_to_client(&rec_to_client, clientIP, rec_from_client->port, NULL);
590      log_it(debug, rec_to_client.body);
591    }
592  return(0);
593}
594
595
596
597/*-----------------------------------------------------------*/
598
599
600
601int handle_progress_rpt(int skt, struct s_client2server_msg_record *rec_from_client, char *clientIP)
602/*
603Purpose:Handle a progress_rpt which has just been received
604    from client.
605Params: skt - client's port to talk to
606    rec_from_client - user record received from client
607    clientIP - client's IP address, in string
608Return: result (0=success, nonzero=failure)
609*/
610{
611//  struct s_server2client_msg_record rec_to_client;
612  int i, res=0;
613  char tmp[MAX_STR_LEN+1];
614
615  i = find_client_in_clientlist(clientIP);
616  if (i < 0)
617    {
618      sprintf(tmp, "Hey, %s isn't logged in. I'm not going to deal with his progress_rpt.", clientIP);
619      log_it(error, tmp);
620      res++;
621    }
622  else
623    {
624      strcpy(g_clientlist.el[i].last_progress_rpt, rec_from_client->body);
625    }
626  return(res);
627}
628
629
630
631/*-----------------------------------------------------------*/
632
633
634
635int handle_user_request(int skt, struct s_client2server_msg_record *rec_from_client, char *clientIP)
636/*
637Purpose:Handle a user request which has just been received
638    from client.
639Params: skt - client's port to talk to
640    rec_from_client - user record received from client
641    clientIP - client's IP address, in string
642Return: result (0=success, nonzero=failure)
643*/
644{
645//  struct s_server2client_msg_record rec_to_client;
646  int i, res=0;
647  char tmp[MAX_STR_LEN+1], command[MAX_STR_LEN+1], first_half[MAX_STR_LEN+1], second_half[MAX_STR_LEN+1], *p;
648
649  i = find_client_in_clientlist(clientIP);
650  if (i < 0)
651    {
652      sprintf(tmp, "Hey, %s isn't logged in. I'm not going to deal with his request.", clientIP);
653      log_it(error, tmp);
654      res++;
655    }
656  else
657    {
658      strcpy(first_half, rec_from_client->body);
659      p = strchr(first_half, ' ');
660      if (!p) { second_half[0]='\0'; } else { strcpy(second_half, p); *p='\0'; }
661      sprintf(command, "echo \"%s %s%s\" > %s", first_half, clientIP, second_half, SERVER_COMDEV);
662      log_it(debug, command);
663      i = system(command);
664      if (i) { res++; log_it(error, "Failed to echo command to FIFO"); }
665    }
666  return(res);
667}
668
669
670
671/*-----------------------------------------------------------*/
672
673
674
675int process_incoming_command(char*incoming)
676/*
677Purpose:Process incoming command, presumably
678        read from FIFO and sent there by sysadm/user.
679Params: incoming - raw command string itself
680Return: result (0=success; nonzero=failure)
681*/
682{
683int res=0, port;
684int clientno;
685char tmp[MAX_STR_LEN+1];
686int pos;
687char command[MAX_STR_LEN+1], ipaddr[MAX_STR_LEN+1],
688    path[MAX_STR_LEN+1], aux[MAX_STR_LEN+1];
689
690//  sprintf(tmp, "incoming = '%s'", incoming);
691//  log_it(debug, tmp);
692          pos=0;
693          sscanf(incoming, "%s %s %s", command, ipaddr, path);
694          if (!strcmp(command, "restore"))
695            { sscanf(incoming, "%s %s %s %s", command, ipaddr, path, aux); }
696          else
697            { aux[0] = '\0'; }
698
699//          for(i=0; i<strlen(command); i++) { command[i]=command[i]|0x60; }
700          sprintf(tmp, "cmd=%s ipaddr=%s path=%s", command, ipaddr, path);
701          log_it(debug, tmp);
702          sprintf(tmp, "%s of %s on %s <-- command received", command, path, ipaddr);
703          log_it(info, tmp);
704          if ((clientno = find_client_in_clientlist(ipaddr)) < 0)
705            {
706              sprintf(tmp, "%s not found in clientlist; so, %s failed.", ipaddr, command);
707              log_it(error, tmp);
708            }
709          else if (g_clientlist.el[clientno].busy == true)
710            {
711              sprintf(tmp, "%s is busy; so, %s failed.", ipaddr, command);
712              log_it(error, tmp);
713            }
714          else
715            {
716              g_clientlist.el[clientno].busy = true;
717              port = g_clientlist.el[clientno].port;
718              if (!strcmp(command, "backup"))
719                { res = backup_client(ipaddr, port, path); }
720              else if (!strcmp(command, "compare"))
721                { res = compare_client(ipaddr, port, path); }
722              else if (!strcmp(command, "restore"))
723                { res = restore_client(ipaddr, port, path, aux); }
724              else
725                {
726                  sprintf(tmp, "%s - cannot '%s'. Command unknown.", ipaddr, command);
727                  log_it(error, tmp);
728                  res=1;
729                }
730              g_clientlist.el[clientno].busy = false;
731            }
732  return(res);
733}
734
735
736
737/*-----------------------------------------------------------*/
738
739
740
741int restore_client(char*ipaddr, int port, char*clientpath, char*auxpath)
742/*
743Purpose:Restore the path of a specific client. Transmit
744the archives from my (the server's) local storage loc.
745Params: clientno - client# in g_clientlist[]
746        clientpath - client's path to be restored
747Return: result (0=success, nonzero=failure)
748*/
749{
750  struct s_server2client_msg_record rec_to_client;
751  int res=0, socket_fd, len, i;
752  char tmp[MAX_STR_LEN+1], infile[MAX_STR_LEN+1];
753  FILE*fin;
754
755  sprintf(tmp, "%s - restoration of %s commencing", ipaddr, clientpath);
756  log_it(info, tmp);
757// FIXME - don't assume the latest backup contains the files we want ;)
758  sprintf(tmp, "find /var/spool/monitas/%s -type f | sort | tail -n1", ipaddr);
759  strcpy(infile, call_program_and_get_last_line_of_output(tmp));
760  sprintf(tmp, "Restoring from data file '%s'", infile);
761  log_it(debug, tmp);
762  if (!does_file_exist(infile)) { log_it(error, "Backup not found. That should be impossible."); return(1); }
763  sprintf(tmp, "Restoring %s - archive=%s", ipaddr, infile);
764  log_it(debug, tmp);
765  rec_to_client.msg_type = trigger_restore;
766  strncpy(rec_to_client.body, clientpath, sizeof(rec_to_client.body));
767  strncpy(rec_to_client.bodyAux, auxpath, sizeof(rec_to_client.bodyAux));
768  if (send_msg_to_client(&rec_to_client, ipaddr, port, &socket_fd))
769    { log_it(error, "restore_client - failed to send msg to client"); return(1); }
770  if (!(fin=fopen(infile, "r")))
771    { log_it(fatal, "Failed to openin temp data file"); }
772  res += transmit_file_to_socket(fin, socket_fd);
773
774  len=read(socket_fd, (char*)&i, sizeof(i));
775  if (!len) { res++; log_it(error, "Client hasn't told me the result of its call to mondorestore"); }
776  else if (len!=sizeof(i)) { res++; log_it(error, "Client didn't sent _entire_ result of its call to mondorestore"); }
777  else if (i) { res++; log_it(error, "Client said, mondorestore returned an error."); }
778  else { log_it(debug, "Client said, mondorestore returned OK"); }
779
780  fclose(fin);
781  close(socket_fd);
782  if (res>0)
783    {
784      sprintf(tmp, "%s - error(s) occurred while restoring %s", ipaddr, clientpath);
785      log_it(error, tmp);
786      rec_to_client.msg_type = restore_fail;
787      sprintf(rec_to_client.body, "Failed to restore %s", clientpath);
788      log_it(debug, rec_to_client.body);
789    }
790  else
791    {
792      sprintf(tmp, "%s - restored %s ok", ipaddr, clientpath);
793      log_it(info, tmp);
794      rec_to_client.msg_type = restore_ok;
795      sprintf(rec_to_client.body, "%s - restored ok", clientpath);
796      log_it(debug, rec_to_client.body);
797    }
798  if (send_msg_to_client(&rec_to_client, ipaddr, port, &socket_fd))
799    {
800      sprintf(tmp, "Unable to notify %s of restore success/failure", ipaddr);
801      log_it(error, tmp);
802      i = find_client_in_clientlist(ipaddr);
803      if (i>=0) { forcibly_logout_client(i); }
804    }
805  return(res);
806}
807
808
809/*-----------------------------------------------------------*/
810
811
812
813int send_msg_to_client(struct s_server2client_msg_record *rec, char *clientIP, int port, int *psocket)
814/*
815Purpose:Send a message from server to client.
816A 'message' could be a response to a login/logout/ping
817request or perhaps a 'trigger'. (A trigger is a message
818from server to client intended to initiate a backup
819or similar activity.)
820Params: rec - record containing the data to be sent to client
821clientIP - the xxx.yyy.zzz.aaa IP address of client
822port - the client's port to send data to
823psocket - returns the socket's file descriptor, left
824open by me; or, if psocket==NULL, then socket is closed
825by me before I return.
826Return: result (0=success, nonzero=failure)
827*/
828{
829struct hostent *hp;
830struct sockaddr_in sin;
831int  s;
832char tmp[MAX_STR_LEN+1];
833if ((hp = gethostbyname(clientIP)) == NULL)
834{
835sprintf(tmp, "send_msg_to_client: %s: unknown host", clientIP);
836log_it(error, tmp);
837return(1);
838}
839memset((void*)&sin, 0, sizeof(sin));
840memcpy((void*)&sin.sin_addr, hp->h_addr, hp->h_length);
841sin.sin_family = AF_INET;
842sin.sin_addr.s_addr = INADDR_ANY;
843sin.sin_port = htons(port);
844if ((s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
845{ perror("socket"); log_it(error, "send_msg_to_client: SOCKET error"); return(1); }
846if (connect(s, (struct sockaddr*)&sin, sizeof(struct sockaddr_in)) < 0)
847{ sprintf(tmp, "Failed to connect to client %s on port %d", clientIP, port); log_it(error, tmp); return(1); }
848send(s, (char*)rec, sizeof(struct s_server2client_msg_record), 0);
849if (psocket) { *psocket=s; } else { close(s); }
850sprintf(tmp, "Sent %s msg [%s] to %s (port %d)", tmsg_to_string(rec->msg_type), rec->body, clientIP, port);
851log_it(debug, tmp);
852return(0);
853}
854
855
856
857/*-----------------------------------------------------------*/
858
859
860
861void start_threads_to_watch_ports_for_requests()
862/*
863Purpose:Start the threads that watch the 'incoming' ports
864    for messages from clients.
865Params: none
866Return: none
867NB: Called by main()
868*/
869{
870  int i, port, res;
871  char tmp[MAX_STR_LEN+1];
872
873  g_clientlist.items = 0;
874  for(i=0; i<NOOF_THREADS; i++)
875    {
876      port = 8700+i;
877      sprintf(tmp,"%d", port);
878      res = pthread_create(&(g_threadinfo[i]), NULL, watch_port_for_requests_from_clients, (void*)tmp);
879      if (res != 0)
880        {
881      perror("Thread creation failed");
882        }
883      usleep(50000);
884    }
885  sprintf(tmp, "Now monitoring ports %d thru %d for requests from clients.", 8700, 8700+NOOF_THREADS-1);
886  log_it(info, tmp);
887}
888
889
890
891/*-----------------------------------------------------------*/
892
893
894
895void terminate_daemon(int sig)
896/*
897Purpose: Shut down the server in response to interrupt.
898Params:  Signal received.
899Returns: None
900*/
901{
902  char command[MAX_STR_LEN+1];
903  static bool logged_out_everyone=false;
904
905  set_signals(false); // termination in progress
906  log_it(info, "Abort signal caught by interrupt handler");
907  if (!logged_out_everyone)
908    {
909// FIXME - lock the var w/mutex
910      logged_out_everyone=true;
911      forcibly_logout_all_clients();
912    }
913/*
914  for(i=0; i<NOOF_THREADS; i--)
915    {
916      sprintf(tmp, "Terminating thread #%d", i);
917      log_it(debug, tmp);
918      pthread_join(g_threadinfo[i], NULL);
919    }
920*/
921  forcibly_logout_all_clients();
922  sprintf(command, "rm -f %s", g_command_fifo);
923  call_program_and_log_output(command);
924//  chmod(g_command_fifo, 0);
925  unlink(g_command_fifo);
926  unlink(g_server_status_file);
927  register_pid(0, "server");
928  log_it(info, "---------- Monitas (server) has terminated ----------");
929  exit(0);
930}
931
932
933
934/*-----------------------------------------------------------*/
935
936
937
938void* watch_port_for_requests_from_clients(void*sz_watchport)
939/*
940Purpose:Watch a port for incoming messages from clients.
941    A 'message' could be a request to login/logout or
942    a ping, or perhaps a request to backup/restore data.
943Params: sz_watchport - the port to watch for incoming messages
944    from clients
945Return: result (0=success, nonzero=failure)
946NB: Function will return nonzero if error occurs during
947    setup but will otherwise run forever, or until killed.
948*/
949{
950  int watch_port;
951  struct sockaddr_in sin;
952  char buf[MAX_STR_LEN+1], tmp[MAX_STR_LEN+1];
953  int len, s, new_s;
954  struct s_client2server_msg_record rec;
955
956  watch_port = atoi((char*)sz_watchport);
957//  sprintf(tmp, "watch_port_for_requests_from_clients(%d) - starting", watch_port); log_it(debug, tmp);
958  memset((void*)&sin, 0, sizeof(sin));
959  sin.sin_family = AF_INET;
960  sin.sin_addr.s_addr = INADDR_ANY;
961  sin.sin_port = htons(watch_port);
962  if ((s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
963    {
964      sprintf(tmp, "Unable to open socket on port #%d", watch_port);
965      log_it(error, tmp);
966      return((void*)-1);
967    }
968  if (bind(s, (struct sockaddr*)&sin, sizeof(sin)) < 0)
969    {
970      sprintf(tmp, "Cannot bind %d - %s\n", watch_port, strerror(errno));
971      log_it(error, tmp);
972      return((void*)-1);
973    }
974  if (listen(s, MAX_PENDING) < 0)
975    {
976      sprintf(tmp, "Cannot setup listen (%d) - %s\n", watch_port, strerror(errno));
977      log_it(error, tmp);
978      return((void*)-1);
979    }
980  /* service incoming connections */
981  sprintf(tmp, "Bound port #%d OK", watch_port);
982  log_it(debug, tmp);
983  while(true)
984    {
985      len = sizeof(sin);
986      if ((new_s = accept(s, (struct sockaddr *)&sin, (unsigned int*)&len)) < 0)
987    {
988      sleep(1);
989      continue;
990    }
991      while ((len = recv(new_s, buf, sizeof(buf), 0)) > 0)
992    {
993      if (len > MAX_STR_LEN) { len = MAX_STR_LEN; }
994      buf[len] = '\0';
995      memcpy((char*)&rec, buf, sizeof(rec));
996      handle_incoming_message(new_s, &sin, &rec);
997    }
998      close(new_s);
999    }
1000  return(NULL);
1001}
1002
1003
1004
1005/*-----------------------------------------------------------*/
1006
1007
1008
1009int main(int argc, char*argv[])
1010/*
1011Purpose: main subroutine
1012Parameters: none
1013Return: result (0=success, nonzero=failure)
1014*/
1015{
1016  pthread_t server_status_thread;
1017
1018  log_it(info, "---------- Monitas (server) by Hugo Rabson ----------");
1019  register_pid(getpid(), "server");
1020  set_signals(true);
1021  start_threads_to_watch_ports_for_requests();
1022  pthread_create(&server_status_thread, NULL, generate_server_status_file_regularly, (void*)SERVER_STATUS_FILE);
1023  create_and_watch_fifo_for_commands(SERVER_COMDEV);
1024  log_it(warn, "Execution should never reach this point");
1025  exit(0);
1026}
1027/* end main() */
1028
1029
Note: See TracBrowser for help on using the repository browser.