#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#define LOG_ERR(func, err) \
do { \
fprintf(stderr, "%s : returned error (%s)\n", func, strerror(err)); \
exit(err); \
} while (0)
int fd;
struct flock lock;
char *buf = "ten bytes!";
char *fname = "/mnt/glusterfs/0/mand.lock";
int open_flags, child, err, status, blocked = 0;
int
do_child(char *argv[])
{
/* Initialize file open flags */
if (strcmp(argv[2], "BLOCK") == 0)
open_flags = O_RDWR;
else if (strcmp(argv[2], "TRUNC") == 0)
open_flags = O_RDWR | O_TRUNC | O_NONBLOCK;
else if (strcmp(argv[2], "NONE") == 0)
open_flags = O_RDWR | O_NONBLOCK;
else
LOG_ERR("Invalid option:", EINVAL);
/* Open the file */
fd = open(fname, open_flags);
if (fd == -1)
LOG_ERR("Child open", errno);
/* Perform the file operation*/
if (strcmp(argv[3], "READ") == 0) {
buf = NULL;
err = read(fd, buf, 10);
if (err == -1)
LOG_ERR("Child read", errno);
} else if (strcmp(argv[3], "WRITE") == 0) {
err = write(fd, buf, 10);
if (err == -1)
LOG_ERR("Child write", errno);
} else if (strcmp(argv[3], "FTRUNCATE") == 0) {
err = ftruncate(fd, 5);
if (err)
LOG_ERR("Child ftruncate", errno);
} else
LOG_ERR("Invalid operation:", EINVAL);
/* Close child fd */
err = close(fd);
if (err)
LOG_ERR("Child close", errno);
/* Exit success */
exit(0);
}
int
main(int argc, char *argv[])
{
if (argc < 4) {
fprintf(stderr,
"Wrong usage: Use as ./mandatory-lock "
"<RD_LCK/WR_LCK> <BLOCK/TRUNC/NONE> "
"<READ/WRITE/FTRUNCATE\n");
exit(EINVAL);
}
/* Create an empty lock file */
fd = open(fname, O_CREAT | O_RDWR, 0755);
if (fd == -1)
LOG_ERR("Parent create", errno);
/* Determine the type of lock */
if (strcmp(argv[1], "RD_LCK") == 0)
lock.l_type = F_RDLCK;
else if (strcmp(argv[1], "WR_LCK") == 0)
lock.l_type = F_WRLCK;
else
LOG_ERR("Parent lock type", EINVAL);
lock.l_whence = SEEK_SET;
lock.l_start = 0L;
lock.l_len = 0L;
/* Let parent acquire the initial lock */
err = fcntl(fd, F_SETLK, &lock);
if (err)
LOG_ERR("Parent lock", errno);
/* Now fork a child */
child = fork();
if (child == 0)
/* Perform the child operations */
do_child(argv);
else {
/* If blocking mode, then sleep for 2 seconds
* and wait for the child */
if (strcmp(argv[2], "NONE") != 0) {
sleep(2);
if (waitpid(child, &status, WNOHANG) == 0)
blocked = 1;
/* Release the parent lock so that the
* child can terminate */
lock.l_type = F_UNLCK;
err = fcntl(fd, F_SETLK, &lock);
if (err)
LOG_ERR("Parent unlock", errno);
}
/* Wait for child to finish */
waitpid(child, &status, 0);
/* Close the parent fd */
err = close(fd);
if (err)
LOG_ERR("Parent close", errno);
/* Remove the lock file*/
err = unlink(fname);
if (err)
LOG_ERR("Parent unlink", errno);
/* If not blocked, exit with child exit status*/
errno = WEXITSTATUS(status);
/* If blocked, exit with corresponding
* error code */
if (blocked)
errno = EWOULDBLOCK;
if (errno != 0)
printf("%s\n", strerror(errno));
exit(errno);
}
}