file locking not working as expected in C - c

I have a file which will be read and write by the multiple process. So to synchronize the file between multiple processes I was using fcntl().
Let's assume we have a file orignal.txt and we have some content in the orignal.txt. Assume the contents in the file are:
show core status
show table status
show action status
Now let's say we have three process A,B,C which would be accessing orignal.txt for deleting the content from it. Say A will delete show core status. B will delete show table status and C will delete show action status.
Actually show core status,show table status and show action status are some commands which is related to particular process, So I am assuming show core status is related to A so whenever A will come he will write show core status into the file orignal.txtand before A terminates it will delete the show core status from the file orignal.txt. Same with other processes also.
So for that I have written the code.
Note: This is a common API for deleting the content from the file which all three processes (A,B,C) will use.
#define ACQUIRE_LOCK 1
#define RELEASE_LOCK 0
int delete_content_from_file(char *command)
{
FILE *fp, *tempFd;
char buf[4096];
int ret;
fp = fopen ("orignal.txt", "r+");
if (fp == NULL)
{
printf("orignal.txt does not exist");
return -1;
}
printf("Acquiring lock for orignal.txt");
ret = fileSync(fp,ACQUIRE_LOCK,F_WRLCK);
if(ret == -1)
{
printf("Failed to acquire lock");
exit(EXIT_FAILURE);
}
tempFd = fopen ("duplicate.txt", "w");
if (tempFd == NULL)
{
printf("Duplicate.txt not exist");
return -1;
}
// read text until newline
while (!feof(fp))
{
if (fgets (buf, sizeof(buf), fp) != NULL)
{
buf[strlen(buf)-1]='\0';
if ((ret = strcmp (command, buf)) != 0) {
fprintf(tempFd, "%s\n", buf);
}
}
fclose(tempFd);
system("cp duplicate.txt orignal.txt");
remove("duplicate.txt");
printf("Releasing lock");
ret = fileSync(fp,RELEASE_LOCK,F_UNLCK);
if(ret == -1)
{
printf("unable to release the lock for orignal.txt");
return -1;
}
fclose (fp);
return 0;
}
int fileSync(FILE *fd,bool flag,short lock_type)
{
struct flock lock;
lock.l_type = lock_type;
lock.l_whence = SEEK_SET; // Starting from the begining of the file
lock.l_start = 0; // no offset
lock.l_len = 0; // to the end of file. 0 means EOF
if(flag == ACQUIRE_LOCK)
{
if(fcntl(fileno(fd), F_SETLKW, &lock) < 0)
{
perror("Error to acquire lock");
return -1;
}
}
else
{
if(fcntl(fileno(fd), F_UNLCK, &lock) < 0)
{
perror("Error releasing lock");
return -1;
}
}
return 0;
}
What I expected : When A will release the lock, the content of the file should become
show table status
show action status
and when B will release the lock, the content of the file should become
show action status
and when C will release the lock, the file will become empty.
But
What I got: When A has released the lock, content of the file is same. All three lines are present. But When B has released the lock , I saw B has deleted the content show table status from the file and same C also deleted the show action status
This is not fixed behaviour, sometimes B does not delete the content but A deletes or C deletes.
I have got struck at this point. Looks to me that file is not getting sync between processes. Any help will be appreciated. Thanks in Advance!

Related

In C, what are some of the most common options to validate parameters and handle errors?

Say I had the following code:
int calcTotalLinesFromFile(FILE *source_file) {
/* Calculate number of lines in source_file and return */
}
int addToFile(char *name_of_file, char *file_content, int line_num) {
FILE *temporary_file;
FILE *source_file;
int temp = 1;
int ch;
if (access(file_name, F_OK) != 0) {
fprintf(stderr, "Cannot add content to file %s: File does not exist.\n", name_of_file);
return errno;
}
source_file = fopen(name_of_file, "r");
if (source_file == NULL) {
perror("Cannot add content to file");
return errno;
}
int total_lines = calcTotalLinesFromFile(source_file);
if (line_num > total_lines) {
fprintf(stderr, "Entered line number exceeds maximum file line.\n");
return -1;
} else if (line_num < 1) {
fprintf(stderr, "Entered line number must be greater than zero.\n");
return -1;
}
temporary_file = fopen("tempfile.tmp", "w");
if (temporary_file == NULL) {
perror("Could not write content to file");
return errno;
}
/* Add content to file */
/* ... */
fclose(source_file);
fclose(temporary_file);
int successful_delete = remove(source_file_name);
if (successful_delete != 0) {
perror("Could not delete source file");
remove("tempfile.tmp"); /* Clean up after failure */
return errno;
}
int successful_rename = rename("tempfile.tmp", source_file_name);
if (successful_rename != 0) {
perror("Could not rename new file");
remove("tempfile.tmp") /* Clean up after failure */
return errno
}
return 0;
}
Here I perform error checks on nearly all functions that could fail so that I can keep the user informed about what exactly is going wrong and to prevent them from inputting data that would throw more errors in the program. My only problem is that it makes my method much much longer than normal, and the method definitely cannot fit on my monitor without having to scroll down. My questions are as follows:
Are there other options for error handling in C?
Am I possibly checking for errors too often?
Is it also common to have the caller validate the parameters before passing them to the method?
Edit: fixed syntax errors.
First, it is not common practice to return errno. Typically functions return -1 and ask the caller to check errno. You especially shouldn't mix the two practices (you return -1 when fprintf fails but you return errno everywhere else). As for other options, one common idiom is using goto for cleanup. (See Valid use of goto for error management in C?)
You can never check errors too often. Nearly every call to the OS can fail somehow, so you should always check. Even fclose can fail.

How do I expose custom files similar to /procfs on Linux?

I have a writer process which outputs its status at regular intervals as a readable chunck of wchar_t.
I would need to ensure the following properties:
When there's and update, the readers shouldn't read partial/corrupted data
The file should be volatile in memory so that when the writer quits, the file is gone
The file content size is variable
Multiple readers could read the file in parallel, doesn't matter if the content is synced, as long as is non partial for each client
If using truncate and then write, clients should only read the full file and not observe such partial operations
How could I implement such /procfs-like file, outside /procfs filesystem?
I was thinking to use classic c Linux file APIs and create something under /dev/shm by default, but I find it hard to implement effectively point 1 and 5 most of all.
How could I expose such file?
Typical solution is to create a new file in the same directory, then rename (hardlink) it over the old one.
This way, processes see either an old one or a new one, never a mix; and it only depends on the moment when they open the file.
The Linux kernel takes care of the caching, so if the file is accessed often, it will be in RAM (page cache). The writer must, however, remember to delete the file when it exits.
A better approach is to use fcntl()-based advisory record locks (typically over the entire file, i.e. .l_whence = SEEK_SET, .l_start = 0, .l_len = 0).
The writer will grab a write/exclusive lock before truncating and rewriting the contents, and readers a read/shared lock before reading the contents.
This requires cooperation, however, and the writer must be prepared to not be able to lock (or grabbing the lock may take undefined amount of time).
A Linux-only scheme would be to use atomic replacement (via rename/hardlinking), and file leases.
(When the writer process has an exclusive lease on an open file, it gets a signal whenever another process wants to open that same file (inode, not file name). It has at least a few seconds to downgrade or release the lease, at which point the opener gets access to the contents.)
Basically, the writer process creates an empty status file, and obtains exclusive lease on it. Whenever the writer receives a signal that a reader wants to access the status file, it writes the current status to the file, releases the lease, creates a new empty file in the same directory (same mount suffices) as the status file, obtains an exclusive lease on that one, and renames/hardlinks it over the status file.
If the status file contents do not change all the time, only periodically, then the writer process creates an empty status file, and obtains exclusive lease on it. Whenever the writer receives a signal that a reader wants to access the (empty) status file, it writes the current status to the file, and releases the lease. Then, when the writer process' status is updated, and there is no lease yet, it creates a new empty file in the status file directory, takes an exclusive lease on it, and renames/hardlinks over the status file.
This way, the status file is always updated just before a reader opens it, and only then. If there are multiple readers at the same time, they can open the status file without interruption when the writer releases the lease.
It is important to note that the status information should be collected in a single structure or similar, so that writing it out to the status file is efficient. Leases are automatically broken if not released soon enough (but there are a few seconds at least to react), and the lease is on the inode – file contents – not the file name, so we still need the atomic replacement.
Here's a crude example implementation:
#define _POSIX_C_SOURCE 200809L
#define _GNU_SOURCE
#include <stdlib.h>
#include <stdarg.h>
#include <inttypes.h>
#include <unistd.h>
#include <fcntl.h>
#include <pthread.h>
#include <signal.h>
#include <limits.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#define LEASE_SIGNAL (SIGRTMIN+0)
static pthread_mutex_t status_lock = PTHREAD_MUTEX_INITIALIZER;
static int status_changed = 0;
static size_t status_len = 0;
static char *status = NULL;
static pthread_t status_thread;
static char *status_newpath = NULL;
static char *status_path = NULL;
static int status_fd = -1;
static int status_errno = 0;
char *join2(const char *src1, const char *src2)
{
const size_t len1 = (src1) ? strlen(src1) : 0;
const size_t len2 = (src2) ? strlen(src2) : 0;
char *dst;
dst = malloc(len1 + len2 + 1);
if (!dst) {
errno = ENOMEM;
return NULL;
}
if (len1 > 0)
memcpy(dst, src1, len1);
if (len2 > 0)
memcpy(dst+len1, src2, len2);
dst[len1+len2] = '\0';
return dst;
}
static void *status_worker(void *payload __attribute__((unused)))
{
siginfo_t info;
sigset_t mask;
int err, num;
/* This thread blocks all signals except LEASE_SIGNAL. */
sigfillset(&mask);
sigdelset(&mask, LEASE_SIGNAL);
err = pthread_sigmask(SIG_BLOCK, &mask, NULL);
if (err)
return (void *)(intptr_t)err;
/* Mask for LEASE_SIGNAL. */
sigemptyset(&mask);
sigaddset(&mask, LEASE_SIGNAL);
/* This thread can be canceled at any cancellation point. */
pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
while (1) {
num = sigwaitinfo(&mask, &info);
if (num == -1 && errno != EINTR)
return (void *)(intptr_t)errno;
/* Ignore all but the lease signals related to the status file. */
if (num != LEASE_SIGNAL || info.si_signo != LEASE_SIGNAL || info.si_fd != status_fd)
continue;
/* We can be canceled at this point safely. */
pthread_testcancel();
/* Block cancelability for a sec, so that we maintain the mutex correctly. */
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
pthread_mutex_lock(&status_lock);
status_changed = 0;
/* Write the new status to the file. */
if (status && status_len > 0) {
const char *ptr = status;
const char *const end = status + status_len;
ssize_t n;
while (ptr < end) {
n = write(status_fd, ptr, (size_t)(end - ptr));
if (n > 0) {
ptr += n;
} else
if (n != -1) {
if (!status_errno)
status_errno = EIO;
break;
} else
if (errno != EINTR) {
if (!status_errno)
status_errno = errno;
break;
}
}
}
/* Close and release lease. */
close(status_fd);
status_fd = -1;
/* After we release the mutex, we can be safely canceled again. */
pthread_mutex_unlock(&status_lock);
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
pthread_testcancel();
}
}
static int start_status_worker(void)
{
sigset_t mask;
int result;
pthread_attr_t attrs;
/* This thread should block LEASE_SIGNAL signals. */
sigemptyset(&mask);
sigaddset(&mask, LEASE_SIGNAL);
result = pthread_sigmask(SIG_BLOCK, &mask, NULL);
if (result)
return errno = result;
/* Create the worker thread. */
pthread_attr_init(&attrs);
pthread_attr_setstacksize(&attrs, 2*PTHREAD_STACK_MIN);
result = pthread_create(&status_thread, &attrs, status_worker, NULL);
pthread_attr_destroy(&attrs);
/* Ready. */
return 0;
}
int set_status(const char *format, ...)
{
va_list args;
char *new_status = NULL;
int len;
if (!format)
return errno = EINVAL;
va_start(args, format);
len = vasprintf(&new_status, format, args);
va_end(args);
if (len < 0)
return errno = EINVAL;
pthread_mutex_lock(&status_lock);
free(status);
status = new_status;
status_len = len;
status_changed++;
/* Do we already have a status file prepared? */
if (status_fd != -1 || !status_newpath) {
pthread_mutex_unlock(&status_lock);
return 0;
}
/* Prepare the status file. */
do {
status_fd = open(status_newpath, O_WRONLY | O_CREAT | O_CLOEXEC, 0666);
} while (status_fd == -1 && errno == EINTR);
if (status_fd == -1) {
pthread_mutex_unlock(&status_lock);
return 0;
}
/* In case of failure, do cleanup. */
do {
/* Set lease signal. */
if (fcntl(status_fd, F_SETSIG, LEASE_SIGNAL) == -1)
break;
/* Get exclusive lease on the status file. */
if (fcntl(status_fd, F_SETLEASE, F_WRLCK) == -1)
break;
/* Replace status file with the new, leased one. */
if (rename(status_newpath, status_path) == -1)
break;
/* Success. */
pthread_mutex_unlock(&status_lock);
return 0;
} while (0);
if (status_fd != -1) {
close(status_fd);
status_fd = -1;
}
unlink(status_newpath);
pthread_mutex_unlock(&status_lock);
return 0;
}
int main(int argc, char *argv[])
{
char *line = NULL;
size_t size = 0;
ssize_t len;
if (argc != 2 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
const char *argv0 = (argc > 0 && argv[0]) ? argv[0] : "(this)";
fprintf(stderr, "\n");
fprintf(stderr, "Usage: %s [ -h | --help ]\n", argv0);
fprintf(stderr, " %s STATUS-FILE\n", argv0);
fprintf(stderr, "\n");
fprintf(stderr, "This program maintains a pseudofile-like status file,\n");
fprintf(stderr, "using the contents from standard input.\n");
fprintf(stderr, "Supply an empty line to exit.\n");
fprintf(stderr, "\n");
return EXIT_FAILURE;
}
status_path = join2(argv[1], "");
status_newpath = join2(argv[1], ".new");
unlink(status_path);
unlink(status_newpath);
if (start_status_worker()) {
fprintf(stderr, "Cannot start status worker thread: %s.\n", strerror(errno));
return EXIT_FAILURE;
}
if (set_status("Empty\n")) {
fprintf(stderr, "Cannot create initial empty status: %s.\n", strerror(errno));
return EXIT_FAILURE;
}
while (1) {
len = getline(&line, &size, stdin);
if (len < 1)
break;
line[strcspn(line, "\n")] = '\0';
if (line[0] == '\0')
break;
set_status("%s\n", line);
}
pthread_cancel(status_thread);
pthread_join(status_thread, NULL);
if (status_fd != -1)
close(status_fd);
unlink(status_path);
unlink(status_newpath);
return EXIT_SUCCESS;
}
Save the above as server.c, then compile using e.g.
gcc -Wall -Wextra -O2 server.c -lpthread -o server
This implements a status server, storing each line from standard input to the status file if necessary. Supply an empty line to exit. For example, to use the file status in the current directory, just run
./server status
Then, if you use another terminal window to examine the directory, you see it has a file named status (with typically zero size). But, cat status shows you its contents; just like procfs/sysfs pseudofiles.
Note that the status file is only updated if necessary, and only for the first reader/accessor after status changes. This keeps writer/server overhead and I/O low, even if the status changes very often.
The above example program uses a worker thread to catch the lease-break signals. This is because pthread mutexes cannot be locked or released safely in a signal handler (pthread_mutex_lock() etc. are not async-signal safe). The worker thread maintains its cancelability, so that it won't be canceled when it holds the mutex; if canceled during that time, it will be canceled after it releases the mutex. It is careful that way.
Also, the temporary replacement file is not random, it is just the status file name with .new appended at end. Anywhere on the same mount would work fine.
As long as other threads also block the lease break signal, this works fine in multithreaded programs, too. (If you create other threads after the worker thread, they'll inherit the correct signal mask from the main thread; start_status_worker() sets the signal mask for the calling thread.)
I do trust the approach in the program, but there may be bugs (and perhaps even thinkos) in this implementation. If you find any, please comment or edit.

Break a file into chunks and send it as binary from client to server in C using winsock?

I created an application that send a text file from client to server
So far i'm send it as string like this:
fp = fopen(filename, "r");
if (fp != NULL) {
newLen = fread(source, sizeof(char), 5000, fp);
if (newLen == 0) {
fputs("Error reading file", stderr);
} else {
source[++newLen] = '\0'; /* Just to be safe. */
}
}else{
printf("The file %s does not exist :(");
return 1;
}
fclose(fp);
send(s , source , strlen(source) , 0); //send file
However my professor told me I must send the file in Binary and be ready to accept a file of any size
I'm trying to figure out how to send the file in binary and break it into chunks
You can copy it one byte at a time.
Reading/writing more than a byte at a time theoretically would make it read and write more efficiently to disk. But since the binary is likely short, and disk I/O is already internally buffered it probably doesn't make a noticeable difference.
perror() is a convenient function that displays the text associated with an error code returned from the most recent UNIX system call. The text in the quotes is the title it displays before showing you the system message associated with the code.
exit(EXIT_FAILURE) exits with a -1 value which is what scripts can test to see if your program succeeded or failed, as the exit status can be retrieved for a UNIX program.
size_t is an integer type, but it's named size_t to give a hint as to what you're using it for.
If you wanted to transfer more data at a time you could. But 1-byte xfers is simple and safe and it works.
FILE *exein, *exeout;
exein = fopen("filein.exe", "rb");
if (exein == NULL) {
/* handle error */
perror("file open for reading");
exit(EXIT_FAILURE);
}
exeout = fopen("fileout.exe", "wb");
if (exeout == NULL) {
/* handle error */
perror("file open for writing");
exit(EXIT_FAILURE);
}
size_t n, m;
unsigned char buff[8192];
do {
n = fread(buff, 1, sizeof buff, exein);
if (n)
m = fwrite(buff, 1, n, exeout);
else
m = 0;
} while ((n > 0) && (n == m));
if (m)
perror("copy");

How to rewrite full content of a file in C

I have text file which uses for ajax source. Every 1 sec browser sends ajax request to read actual data from this file.
Also I have deamon written on C which writes actual data to that file. Look at the following code:
static void writeToFile_withLock(const char * file_path, const char * str)
{
struct flock fl = {F_WRLCK, SEEK_SET, 0, 0, 0 };
int fd;
const char * begin = str;
const char * const end = begin + strlen(str);
fl.l_pid = getpid();
if ((fd = open(file_path, O_CREAT | O_WRONLY)) == -1) {
perror("open");
exit(1);
}
printf("Trying to get lock...\n");
if (fcntl(fd, F_SETLKW, &fl) == -1) {
perror("fcntl");
exit(1);
}
printf("got lock\n");
printf("Try to write %s\n", str);
while (begin < end)
{
size_t remaining = end - begin;
ssize_t res = write(fd, begin, remaining);
if (res >= 0)
{
begin += res;
continue; // Let's send the remaining part of this message
}
if (EINTR == errno)
{
continue; // It's just a signal, try again
}
// It's a real error
perror("Write to file");
break;
}
fl.l_type = F_UNLCK; /* set to unlock same region */
if (fcntl(fd, F_SETLK, &fl) == -1) {
perror("fcntl");
exit(1);
}
printf("Unlocked.\n");
close(fd);
}
The problem: If former data was > the new data then old several symbols keeped at the end of the file.
How I can rewrite full file content?
Thanks in advance.
Add O_TRUNC to the open() call...
O_TRUNC
If the file already exists and is a regular file and the open mode
allows writing (i.e., is O_RDWR or O_WRONLY) it will be truncated to
length 0. If the file is a FIFO or terminal device file, the O_TRUNC
flag is ignored. Otherwise the effect of O_TRUNC is unspecified.
You basically have two options. Either set the O_TRUNC bit of the 2nd parameter of open to discard all content when you open the file, or call ftruncate when you are finished to discard the content of the file that you do not want. (Or use truncate, but since you already have an open file descriptor, there's no advantage to doing that.)

Why does writing to a file descriptor after the target file has been deleted succeed?

code:
int main(int argc, char **argv)
{
int fd = open("test.txt", O_CREAT|O_RDWR, 0200|0400);
if(fd == -1)
{
printf("failure to oepn");
exit(-1);
}
int iRet = write(fd, "aaaaaaaaaa", 10);
if(iRet == -1)
{
printf("failure to writer");
exit(-1);
}
sleep(10);
printf("You must remove");
iRet = write(fd, "bbbbbbbbbb", 10);
if(iRet == -1)
{
printf("failure to after writer");
exit(-1);
}
exit(0);
}
during the sleep(), you delete the test.txt, but the process write successful!why?
if a log ”Singleton“ instance, you remove the file on the disk.write is successful, but you can get nothing.
class log
{
public:
void loggerWriter(std::string str);
int fd;
};
log::log(std::string filename):fd(-1)
{
fd = open(filename.c_str(), O_CREAT|)
//...
}
log::loggerWriter(std::string str)
{
writer(fd, str.c_str(), str.size());
}
int main()
{
log logger("text.txt");
//...
//I want to know the text.txt the text.txt have delete on the disk or not.
//if delete i can create another file to log.
}
"unlink" cann't solve this problem.
The manual page for unlink(2) states clearly:
unlink() deletes a name from the file system. If that name was the
last link to a file and no processes have the file open the file is
deleted and the space it was using is made available for reuse.
If the name was the last link to a file but any processes still have
the file open the file will remain in existence until the last file
descriptor referring to it is closed.
As caf excellently notes in the comments:
The write() is successful because it writes to the file, which still
exists at this point even though it no longer has a name. The filename
and the file itself are distinct, and have separate lifetimes.

Resources