Skip to content

Instantly share code, notes, and snippets.

@mrbid
Last active August 19, 2022 13:10
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mrbid/9db409461e43ae5336c87bc8048430a1 to your computer and use it in GitHub Desktop.
Save mrbid/9db409461e43ae5336c87bc8048430a1 to your computer and use it in GitHub Desktop.
Mission critical file increment helper process. Variant 3.
/*
James William Fletcher (github.com/mrbid)
August 2021
Mission critical file increments. Variant 3.
https://james-william-fletcher.medium.com/mission-critical-integer-increment-operations-from-php-c1c71fb42451
This file is compiled to a program that any
service can externally execute to ensure
a file increment or decrement operation
never fails.
Assuming that there is no condition where the
operating system will terminate the process
and that your max process limit;
/proc/sys/kernel/pid_max is not exceeded.
Integers are processed as long long with a maximum
value of 9223372036854775807, and are saved in string
format as a maximum of 19 bytes long.
*/
#include <sys/file.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#define CHMOD 0666
#define MAX_BUFF 20 // +1 for the null terminator
#define MAX_STRIKES 333 // total failure iterations before program exit
#define ALLOW_WRITE_FAIL 1
int doStrikeout(const unsigned int reset)
{
sleep(1); // sleep for 1 second between each stike
static unsigned int strikes = 0;
if(reset == 1)
strikes = 0;
strikes++;
if(strikes > MAX_STRIKES)
return 1;
return 0;
}
int main(int argc, char *argv[])
{
// check all params supplied
if(argc < 3)
{
printf("Incorrect arguments.\n");
return 0;
}
// temporary buffer for file read
char tb[MAX_BUFF] = {0};
// open file
int f = open(argv[1], O_CREAT | O_RDWR, CHMOD);
doStrikeout(1); // reset strikeout
while(f < 0)
{
f = open(argv[1], O_CREAT | O_RDWR, CHMOD);
if(doStrikeout(0) == 1)
goto end;
}
// lock file
doStrikeout(1); // reset strikeout
while(flock(f, LOCK_EX) < 0)
if(doStrikeout(0) == 1)
goto end;
// get file size in bytes
off_t len = lseek(f, (size_t)0, SEEK_END);
doStrikeout(1); // reset strikeout
while(len < 0)
{
len = lseek(f, (size_t)0, SEEK_END);
if(doStrikeout(0) == 1)
goto end;
}
// check size of read fits into memory
// this should never fail but if it
// does we should exit the process or
// it would hang forever and eventually
// the system max process limit would
// be exceeded.
if(len >= MAX_BUFF)
{
printf("len >= MAX_BUFF\n");
goto end;
}
// seek back to beginning of file
doStrikeout(1); // reset strikeout
while(lseek(f, (size_t)0, SEEK_SET) < 0)
if(doStrikeout(0) == 1)
goto end;
// read file
doStrikeout(1); // reset strikeout
while(read(f, &tb, len) != len)
if(doStrikeout(0) == 1)
goto end;
// convert to long long and increment
const long long oll = atoll(tb); // backup encase write fails
long long ll = oll;
ll += atoll(argv[2]);
// set temp buff to write data
sprintf(tb, "%lld", ll);
const int wlen = strlen(tb);
// trunc
doStrikeout(1); // reset strikeout
while(ftruncate(f, 0) < 0)
if(doStrikeout(0) == 1)
goto end;
// seek
doStrikeout(1); // reset strikeout
while(lseek(f, (size_t)0, SEEK_SET) < 0)
if(doStrikeout(0) == 1)
goto end;
// write
#if ALLOW_WRITE_FAIL == 1
doStrikeout(1); // reset strikeout
while(write(f, tb, wlen) != wlen)
{
if(doStrikeout(0) == 1) // write has failed
{
// try to restore original file state
sprintf(tb, "%lld", oll);
const int wlen = strlen(tb);
doStrikeout(1); // reset strikeout
while(write(f, tb, wlen) != wlen)
if(doStrikeout(0) == 1)
goto end; // catastropic failure
}
}
#else
while(write(f, tb, wlen) != wlen)
sleep(1);
#endif
//end
end:
// unlock
flock(f, LOCK_UN);
//done
fsync(f);
close(f);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment