/* * Copyright 2004 - Eric Kerin * * Permission to use, copy, modify, and distribute this software and its * documentation for any purpose, without fee, and without a written agreement * is hereby granted, provided that the above copyright notice and this * paragraph appear in all copies. * * Use at your own risk. * * Description: A Very quick and dirty hack up to implement log shipping with pgsql 8.0 * It's full of bugs, and barely works. It requires a few kills in selective spots to * keep it moving at the right times (like when it's asked for .backup files) * * Note: This version is poorly written and obsolete, and only kept for historical purposes. * You want the fresh version at http://www.bootseg.com/log_ship.c */ #include #include #include #include #include #include #include void sig_catch(int val){ if(val == SIGHUP){ exit(1); } } void display_usage(){ fprintf(stderr,"Usage:\n"); fprintf(stderr,"\t log_ship \n"); fprintf(stderr,"\t direction is one of -a (archive) -r (restore)\n"); fprintf(stderr,"\t in archive mode, the file is copied to the archive directory\n"); fprintf(stderr,"\t in restore mode, the given file is copied once it exists to the directory specified\n"); fprintf(stderr,"\t\tThe way to have this program return failure while in restore mode is by sending SIGHUP\n"); } int is_historyfile(char *filename){ if(strstr(filename,".history") == 0){ return 0; }else{ return 1; } } int main(int argc, char *argv[]){ if(argc<5){ fprintf(stderr,"ERROR: 4 arguements are required\n"); display_usage(); exit(1); } if(strncmp(argv[1],"-a",2) == 0){ //yes I know insecure, crappy, and wrong, I'll fix it before this is of any real use. char cmd1[1000]; char *cp; FILE *control; int res; //copy the file into the archive directory strcpy(cmd1,"cp "); strcat(cmd1,argv[3]); strcat(cmd1," "); strcat(cmd1,argv[2]); strcat(cmd1,"/"); strcat(cmd1,argv[4]); res = system(cmd1); if(res != 0){ fprintf(stderr,"ERROR: Copy Failed! Returning Failure\n"); exit(1); } //update the control file with this archive's filename //build the name of the control file strcpy(cmd1,argv[2]); strcat(cmd1,"/logship_control"); //open it up for writing, create if necessary, and position at the begining of the file if((control = fopen(cmd1,"w+")) == NULL){ fprintf(stderr,"ERROR: Could not open control file: %s\n",cmd1); exit(1); } //write out the name of the last file written res = fprintf(control,"%s",argv[4]); if(res < strlen(argv[4])){ fprintf(stderr,"ERROR: Control file update failed to write data\n"); exit(1); } //close the file res = fclose(control); // POSSIBLE PROBLEM HERE, If the close failed, did the file get updated...or not... and should we fsync? if(res != 0){ fprintf(stderr,"ERROR: Could not close control file, update considered failed\n"); exit(1); } //if we're here, everything should have gone to plan. exit(0); }else if(strncmp(argv[1],"-r",2) == 0){ char *membuf = 0; struct stat statbuf; int res; int control; void *old_handler; old_handler = signal(SIGHUP, &sig_catch); if(old_handler == SIG_ERR){ fprintf(stderr,"ERROR: failed to hook signal handler for SIGHUP\n"); exit(1); } membuf = (char *)malloc(strlen(argv[2])+1+strlen(argv[3])+1+strlen(argv[4])+30); //the plus 30 is for good measure ;) if(membuf == 0){ fprintf(stderr,"ERROR: Out of Memory\n"); exit(1); } while(1){ //calculate the source filename strcpy(membuf,argv[2]); strcat(membuf,"/"); strcat(membuf,argv[3]); //check for the file that the postmaster wants, sleep till it's there res = stat(membuf,&statbuf); if(res != 0){ res = errno; if(res == ENOENT){ if(is_historyfile(argv[3])){ //it's a .history file it's asking for, just say it isn't there exit(ENOENT); }else{ //not there, sleepy time sleep(1); } continue; }else{ fprintf(stderr,"ERROR: %s\n",strerror(res)); exit(1); } } if(!is_historyfile(argv[3])){ //get the current max log from the control file, sleep if it's not ready yet strcpy(membuf,argv[2]); strcat(membuf,"/logship_control"); //open it up for reading, and position at the begining of the file if((control = open(membuf,O_RDONLY)) == -1){ fprintf(stderr,"ERROR: Could not open control file %s: %s\n",membuf,strerror(errno)); exit(1); } memset(membuf,0,(strlen(argv[2])+1+strlen(argv[3])+1+strlen(argv[4])+30)); res = read(control,membuf,24); if(res != 24){ fprintf(stderr,"ERROR: Only got %d characters from the control file, was expecting 24: %s\n",res,strerror(errno)); exit(1); } //see if it's ok to use this file printf("Compare: %s to %s: %d\n",argv[3],membuf,strcmp(argv[3],membuf)); if(strcmp(argv[3],membuf) > 0 ){ fprintf(stderr,"File %s isn't ready accordig to the control file which is at %s\n",argv[3],membuf); sleep(5); //sleep for a sec, we're not ready to use this file now continue; //try again } //close the file res = close(control); if(res == -1){ fprintf(stderr,"ERROR: Could not close control file: %s\n",strerror(errno)); exit(1); //this is perhaps non-fatal durring restore } } //ok we have our file and it passed the control check, copy it where it needs to go strcpy(membuf,"cp "); strcat(membuf,argv[2]); strcat(membuf,"/"); strcat(membuf,argv[3]); strcat(membuf," "); strcat(membuf,argv[4]); res = system(membuf); if(res != 0){ fprintf(stderr,"ERROR: Copy Failed! Returning Failure\n"); exit(res); } exit(0); } exit(0); }else{ fprintf(stderr,"ERROR: %s is not a valid direction\n",argv[1]); display_usage(); exit(1); } }