/* * LIVED - Live Daemon. * v0.01 * * By Carl Ritson - 2001 * Licensed under GPL2 - www.gnu.org * * Works on a Master+Slave System, sends heartbeats, when the master stops * Sending Heartbeats or sends an explicit ASSERT signal to the slave, * the slave takes over. When the master starts sending heartbeats again, * or the slave receives an explicit DROP signal it drops and lets the * master do the work. * The DROP and ASSERT behaviors are defined by user scripts, * see lived.conf. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #define DEBUG 0 #define BUFSIZE 1024 struct in_addr peeraddr; int maxtokens = 0; int tokens = 0; int heartrate = 0; u_int16_t port = 0; int master = 0; char *password = NULL; char *assert_script = NULL; char *drop_script = NULL; char *listenaddr = NULL; int spin = 0; int main_soc = 0; enum { HEARTBEAT = 101, ASSERT = 202, DROP = 303 }; int get_line(FILE *file, char *buffer, int length) { int c = fgetc(file); int r = 0; while(c != EOF && r < (length - 1) && (char)c != '\n') { (*buffer) = (char)c; ++buffer; ++r; c = fgetc(file); } *buffer = '\0'; if (DEBUG) {fprintf(stderr,"\tGet_line\n");} if (c == EOF) { return c; } else { return r + 1; } } void load_config(char *filename) { char buffer[BUFSIZE]; char *item; char *var; FILE *config; config = fopen(filename,"r"); if(config == NULL) { fprintf(stderr,"Failed to config open %s\n",filename); exit(1); } else { while(get_line(config,buffer,BUFSIZE) != EOF) { if(*buffer != '#' && *buffer != '\0') { item = strtok(buffer," "); var = strtok(NULL," "); if(strcasecmp(item,"heartrate") == 0) { heartrate = atoi(var); } else if(strcasecmp(item,"peeraddr") == 0) { inet_aton(var,&peeraddr); } else if(strcasecmp(item,"maxtokens") == 0) { maxtokens = atoi(var); } else if(strcasecmp(item,"port") == 0) { port = htons(atoi(var)); } else if(strcasecmp(item,"password") == 0) { password = strdup(var); } else if(strcasecmp(item,"assert_script") == 0) { assert_script = strdup(var); } else if(strcasecmp(item,"drop_script") == 0) { drop_script = strdup(var); } else if(strcasecmp(item,"master") == 0) { master = atoi(var); } else if(strcasecmp(item,"listenaddr") == 0) { listenaddr = strdup(var); } } } fclose(config); } } int process_message(char *buffer) { int i; char *temp; temp = strtok(buffer," "); if(strcmp(temp,password) == 0) { temp = strtok(NULL," "); i = atoi(temp); } return i; } void sleep_loop() { char buffer[BUFSIZE]; struct sockaddr_in *from; struct sockaddr_in *expect; int in_len = sizeof(struct sockaddr_in); int r; int act; int gotbeat = 0; int gotassert = 0; int temp; /* Allocate and Zero 2 Address Structures */ from = malloc(in_len); expect = malloc(in_len); memset(from,'\0',in_len); memset(expect,'\0',in_len); /* Setup the expect structure to what we expect */ expect->sin_family = AF_INET; expect->sin_port = port; memcpy(&expect->sin_addr,&peeraddr,sizeof(struct in_addr)); /* Fill the token bucket */ tokens = maxtokens; for(;;) { sleep(heartrate); temp = in_len; gotbeat = 0; do { r = recvfrom(main_soc,buffer,BUFSIZE,MSG_DONTWAIT,(struct sockaddr *)from,&temp); if(r > 0 && r != EAGAIN) { if(memcmp(from,expect,temp) == 0) { act = process_message(buffer); if(act == HEARTBEAT) { gotbeat = 1; } else if(act == ASSERT) { gotassert = 1; } else if(act == DROP) { gotassert = 0; } } } } while(r > 0 && r != EAGAIN); if(gotbeat == 0) { --tokens; } else if(tokens < maxtokens) { ++tokens; } if(gotassert || tokens <= 0) { if(gotassert) { syslog(LOG_WARNING,"Got assert signal from peer %s",inet_ntoa(peeraddr)); sleep((int)(heartrate/2)); } else { syslog(LOG_CRIT,"Token Bucket for peer %s Empty",inet_ntoa(peeraddr)); } break; } } free(expect); free(from); } void awake_loop() { char buffer[BUFSIZE]; char beat[BUFSIZE]; struct sockaddr_in *from; struct sockaddr_in *expect; int in_len = sizeof(struct sockaddr_in); int beat_len; int r; int act; int gotdrop = 0; int temp; /* Prepare Heatbeat Message */ sprintf(beat,"%s %d",password,HEARTBEAT); beat_len = strlen(beat); /* Allocate and Zero 2 Address Structures */ from = malloc(in_len); expect = malloc(in_len); memset(from,'\0',in_len); memset(expect,'\0',in_len); /* Setup the expect structure to what we expect */ /* Expect Structure is also used for sending heartbeats */ expect->sin_family = AF_INET; expect->sin_port = port; memcpy(&expect->sin_addr,&peeraddr,sizeof(struct in_addr)); for(;;) { temp = in_len; sleep(heartrate); sendto(main_soc,beat,beat_len,0,(struct sockaddr *)expect,in_len); do { r = recvfrom(main_soc,buffer,BUFSIZE,MSG_DONTWAIT,(struct sockaddr *)from,&temp); if(r > 0 && r != EAGAIN) { if(memcmp(from,expect,temp) == 0) { act = process_message(buffer); if(act == DROP) { gotdrop = 1; } else if(act == ASSERT) { gotdrop = 0; } else if(act == HEARTBEAT && !master) { gotdrop = 1; } } } } while(r > 0 && r != EAGAIN); if(gotdrop) { syslog(LOG_WARNING,"Got drop signal from peer %s",inet_ntoa(peeraddr)); break; } } free(expect); free(from); } void assert() { pid_t p; syslog(LOG_CRIT,"Asserting (%s)",assert_script); p = fork(); if(p == 0) { execve(assert_script,NULL,NULL); exit(0); } else { waitpid(p,NULL,0); } } void drop() { pid_t p; syslog(LOG_CRIT,"Dropping (%s)",drop_script); p = fork(); if(p == 0) { execve(drop_script,NULL,NULL); exit(0); } else { waitpid(p,NULL,0); } } void send_assert() { char buffer[BUFSIZE]; struct sockaddr_in *peer = malloc(sizeof(struct sockaddr_in)); /* Prepare Address Structure */ peer->sin_family = AF_INET; peer->sin_port = port; memcpy(&peer->sin_addr,&peeraddr,sizeof(struct in_addr)); /* Prepare ASSERT Message */ sprintf(buffer,"%s %d",password,ASSERT); /* Send 2 Copies 2 Seconds Apart */ sendto(main_soc,buffer,strlen(buffer),0,(struct sockaddr *)peer,sizeof(struct sockaddr_in)); sleep(2); sendto(main_soc,buffer,strlen(buffer),0,(struct sockaddr *)peer,sizeof(struct sockaddr_in)); free(peer); syslog(LOG_WARNING,"Sent Assert Signal to Peer %s",inet_ntoa(peeraddr)); } void send_drop() { char buffer[BUFSIZE]; struct sockaddr_in *peer = malloc(sizeof(struct sockaddr_in)); /* Prepare Address Structure */ peer->sin_family = AF_INET; peer->sin_port = port; memcpy(&peer->sin_addr,&peeraddr,sizeof(struct in_addr)); /* Prepare Drop Message */ sprintf(buffer,"%s %d",password,DROP); /* Send 2 Copies 2 Seconds Apart */ sendto(main_soc,buffer,strlen(buffer),0,(struct sockaddr *)peer,sizeof(struct sockaddr_in)); sleep(2); sendto(main_soc,buffer,strlen(buffer),0,(struct sockaddr *)peer,sizeof(struct sockaddr_in)); free(peer); syslog(LOG_WARNING,"Sent Drop Signal to Peer %s",inet_ntoa(peeraddr)); } void main_loop() { if(master) { for(;;) { send_drop(); sleep(heartrate); assert(); awake_loop(); } } else { for(;;) { drop(); sleep_loop(); assert(); awake_loop(); } } } int sigtermhandler(int s) { syslog(LOG_WARNING,"Received Term Signal"); if(master) { send_assert(); drop(); } closelog(); exit(0); return 0; } void setup_signals() { signal(SIGTERM,(void *)&(sigtermhandler)); signal(SIGSTOP,(void *)&(sigtermhandler)); signal(SIGQUIT,(void *)&(sigtermhandler)); } void setup_socket() { struct sockaddr_in addr; int ret; main_soc = socket(PF_INET,SOCK_DGRAM,0); addr.sin_family = AF_INET; addr.sin_port = port; if(listenaddr != NULL) { inet_aton(listenaddr,&addr.sin_addr); } else { inet_aton("0.0.0.0",&addr.sin_addr); } ret = bind(main_soc,(struct sockaddr *)&addr,sizeof(addr)); if(ret != 0) { fprintf(stderr,"Bind Port %d Failed\n",ntohs(port)); exit(1); } } void drop_to_background() { freopen("/dev/null","r",stdin); freopen("/dev/null","a",stdout); freopen("/dev/null","a",stderr); } int signalusr2(int s) { spin = 0; return s; } void setup_log() { openlog("lived",0,LOG_DAEMON); syslog(LOG_NOTICE,"started pid:%d listening on:%s port:%d as %s",getpid(),listenaddr,ntohs(port),master?"Master":"Slave"); } int main(int argc, char *argv[]) { pid_t p = 0; pid_t parent = getpid(); if(argc < 2) { fprintf(stderr,"You must supply a config\n"); exit(1); } load_config(argv[1]); if(assert_script == NULL || drop_script == NULL || password == NULL) { fprintf(stderr,"Config is incomplete\n"); exit(1); } /* Register SIGUSR1 to spin unlock function */ signal(SIGUSR2,(void *)&(signalusr2)); p = fork(); if(p == 0) { drop_to_background(); setup_socket(); setup_signals(); setup_log(); /* Send parent SIGUSR1 to say we are ready */ kill(parent,SIGUSR2); main_loop(); } else { printf("Dropping into Background\n"); fflush(stdout); /* Child sends SIGUSR1 when ready */ spin = 1; while(spin) { parent = waitpid(p,NULL,WNOHANG); if(parent != 0) { break; } usleep(10000); } if(spin) { fprintf(stderr,"Fail to Drop to Background\n"); return 1; } else { printf("Dropped into Background as : %d\n",p); return 0; } } /* Droppped off the end so return 1 */ return 1; }