I need to write to and read from a config file using shell CGI, and I also have a C program that must read and modify the config file every few seconds. I tried using flock to maintain data integrity and prevent race conditions, but both CGI and C program are still able to access the file at the same time.
If I use 2 C programs on the file it works, but not both the shell CGI and C program.
This is the C program am using:
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
int main() {
FILE *file;
struct flock fl = {0};
char buff[1000];
file = fopen("/home/siddusuhaas/sample.conf", "r+");
if (file == NULL) {
perror("Error opening file");
return 1;
}
printf("Opened File\n");
// Set an exclusive lock on the entire file
fl.l_type = F_WRLCK;
fl.l_whence = SEEK_SET;
fl.l_start = 0;
fl.l_len = 0;
if (fcntl(fileno(file), F_SETLKW, &fl) == -1) {
perror("Error locking file");
fclose(file);
return 1;
}
printf("File Lock Successful\n");
// Perform operations on the locked file here
system("lslocks | grep 'bcf.conf'");
// Read the file contents
size_t nread = fread(buff, sizeof(char), sizeof(buff) - 1, file);
if (nread == 0 && !feof(file)) {
perror("Error reading file");
fclose(file);
return 1;
}
buff[nread] = '\0'; // Null-terminate the buffer
printf("Data is %zu\t %.*s\n", nread, (int)nread, buff);
for (int i = 0; i < 10; i++) {
system("lslocks | grep 'bcf.conf'");
sleep(1);
fseek(file , 0 , SEEK_SET);
memset(buff, 0, sizeof(buff));
nread = fread(buff, sizeof(char), sizeof(buff) - 1, file);
if (nread == 0 && !feof(file)) {
perror("Error reading file");
fclose(file);
return 1;
}
buff[nread] = '\0'; // Null-terminate the buffer
printf("Data is %zu\t %.*s\n", nread, (int)nread, buff);
}
fl.l_type = F_UNLCK;
if (fcntl(fileno(file), F_SETLK, &fl) == -1) {
perror("Error unlocking file");
fclose(file);
return 1;
}
printf("File Unlocked Successfully\n");
fclose(file);
return 0;
}
And this is my CGI:
#!/bin/bash
file=/home/siddusuhaas/sample.conf
exec 200>>"$file"
if ! flock -x 200; then
echo "Unable to acquire lock on file"
exit 1
fi
echo "Surprise! The file is locked!" > "$file"
#Release the lock
exec 200>&-
The flock(1)
program uses the flock(2)
system call to lock files. Your C program is using fcntl(2)
to lock files. Those are two different locking mechanisms that don't interact with each other on Linux. Change the C program to also use flock(2)
locks.
I would also drop the use of stdio functions for reading and writing the file; the interaction between stdio buffering and when underlying files are actually read and written is potentially a source of subtle and hard to reproduce bugs in the presence of multiple processes modifying the same file. Keep it simple and just use open()
/read()
/write()
/etc.