It is a common issue, that the developer wants to limit the application (process, task or daemon all equivalently used) to run only one single instance at once. Some devices (file descriptors) can be accessed by one process at a time. Usually, a file lock is used to indicate that something is in use. Within an application, between separate threads of the same, a mutex can be used to lock shared memory.

On a Linux the kernel provides the /proc filesystem. It's a folder where there are as many subfolders as running tasks, identified by the process identifier the PID. (It's simply a 4-5 digit number. You can find out the PID of a process by the bash tools pgrep or top)

So, if you know the PID of the process, you can verify whether it's running or not. The pactice for daemons (background processes) is to place a lockfile or an empty folder at /var/run.

Let's call our application ads. (this is the name of the executable). The procedure I implemented is as follows.

  1. When the application starts, it checks weather the lockfile /var/run/ads.pid exists or not. (stat() function)
  2. If there is no lockfile, we might already start our application -> step 5
  3. Let's assume there is a lockfile with content 3512, but the directory /proc/3512/ does not exist, i.e. the application is not running -> step 5
  4. There is a lockfile with 3512 in it, and /proc/3512/ exists -> we have to exit, since one instance of the same is already running. (or a fake application pretending some other's pid)
  5. We might let to start the application, however a further check can be implemented, to ask the system if a process identified by a known name is running (e.g. as the shell command pgrep ^ads$ does)
  6. We create the new lockfile with the PID of the actual process.
src/filelock.c
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
/*
 * parameter filelock: the name of the lockfile to check.
 *           e.g.: "/var/run/ads.pid"
 */
void filelock(const char *filelock) 
{
    pid_t pid;
    int fd;
    struct stat sts;
    char chpid[10];
    char runpid[14];

    if (stat(filelock, &sts)==0){ // filelock already exists 
        printf("filelock already exists!\n");
        if ( (fd=open(filelock, O_RDONLY))==-1 ){
            perror("failed to open filelock for reading!");
            deletefile(filelock);
        }

        if (stat(filelock, &sts)==0){  // if filelock still exists 
            if (read(fd,chpid,sizeof(chpid))<=2){ // does not contain data 
                printf("filelock does not contain valid PID!\n");
                close(fd);
                deletefile(filelock);
            } else {
                close(fd);

                sprintf(runpid,"/proc/%d/",atoi(chpid));
                if (stat(runpid, &sts)==0){ //todo: maybe add date check
                    printf("process %s seems to run already!\n",runpid);    
                    exit(1);
                } else  {//delete broken pid file
                    printf("deleting broken filelock (since process died)!\n");
                    deletefile(filelock);
                }
            }
        }
    }


    if ((fd = open(filelock, O_RDWR | O_EXCL | O_CREAT , S_IRUSR | 
            S_IWUSR | S_IRGRP)) == -1) {
        close(fd);             
        perror("failed to open filelock for exclusive reading");
        exit(1);
    } else {
        pid = getpid();
        sprintf(chpid,"%ld",(long)(pid));
        printf("will acquire new filelock for PID=%s\n",chpid);
        if (write(fd,chpid,strlen(chpid)) != strlen(chpid)){
            close(fd);
            perror("failed to write to filelock");
            exit(1);
        } else {
            close(fd); 
            printf("OK\n");    
        }
    }
}



void deletefile(const char *fl)
{
    if (unlink(fl)==-1){
        perror("failed to remove filelock");
        exit(1);                
    }
    printf("invalid filelock found, deleted!\n");    
}

Source on Github: filelock.c



Comments

comments powered by Disqus