1 #define _XOPEN_SOURCE 500
2 #include <sys/types.h>
3 #include <signal.h>
4 #include <sys/stat.h>
5 #include <unistd.h>
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <string.h>
9 #include <assert.h>
10 #include <errno.h>
11 
12 struct lock_file {
13   char * file;
14   struct lock_file * next;
15 };
16 
17 static struct lock_file * lf_list = NULL;
18 
free_lockfile(struct lock_file * pre,struct lock_file * cur)19 static void free_lockfile(struct lock_file * pre, struct lock_file * cur)
20 {
21   if (cur == lf_list)
22     {
23       lf_list = cur->next;
24     }
25   else
26     {
27       assert(pre != cur);
28       pre->next = cur->next;
29     }
30   free(cur->file); free(cur);
31   return;
32 }
33 
free_this_lockfile(char * file)34 static void free_this_lockfile(char * file)
35 {
36   struct lock_file * pre = lf_list;
37   struct lock_file * cur = lf_list;
38 
39   while (cur)
40     {
41       if (0 == strcmp(cur->file, file))
42 	{
43 	  free_lockfile(pre, cur);
44 	  return;
45 	}
46       pre = cur;
47       cur = cur->next;
48     }
49   return;
50 }
51 
add_this_lockfile(char * file)52 static void add_this_lockfile(char * file)
53 {
54   struct lock_file * cur = malloc(sizeof(struct lock_file));
55 
56   if (cur)
57     {
58       size_t len = 1 + strlen(file);
59       cur->file = malloc(len);
60       strcpy(cur->file, file);
61       /* cur->file = strdup(file); *//* osx doesn't have it */
62       cur->next = lf_list;
63       lf_list   = cur;
64     }
65   return;
66 }
67 
remove_all_locks()68 void remove_all_locks()
69 {
70   struct lock_file * cur = lf_list;
71 
72   while (cur)
73     {
74       unlink(cur->file);
75       cur = cur->next;
76     }
77   return;
78 }
79 
80 
write_pid(int fd)81 static int write_pid(int fd)
82 {
83   pid_t  pid      = getpid();
84   FILE * fp;
85 
86   fp = fdopen(fd, "w");
87   if (!fp)
88     {
89       perror("write_pid: fdopen");
90       return -1;
91     }
92 
93   if (fprintf(fp, "%d", (int) pid) < 0)
94     {
95       perror("write_pid: fprintf");
96       return -1;
97     }
98 
99   if (0 != fclose(fp))
100     {
101       perror("write_pid: fclose");
102       return -1;
103     }
104   return 0;
105 }
106 
pid_valid(int pid)107 static int pid_valid(int pid)
108 {
109   return kill(pid, 0);
110 }
111 
test_lock(char * lockfile)112 static int test_lock(char * lockfile)
113 {
114   int    fpid;
115   int    pid  = (int) getpid();
116   FILE * fp   = fopen(lockfile, "r");
117 
118   if (!fp)
119     {
120       perror("test_lock: fopen");
121       return -1;
122     }
123   if (1 != fscanf(fp, "%d", &fpid))
124     {
125       perror("test_lock: fscanf");
126       fclose(fp);
127       return -1;
128     }
129   if (pid != fpid)
130     {
131       fclose(fp);
132       return pid_valid(fpid);
133     }
134 
135   fclose(fp);
136   return 0;
137 }
138 
try_lock(char * template,char * lockfile)139 static int try_lock(char * template, char * lockfile)
140 {
141   int ret = 0;
142   int ttt = 5;
143 
144   do {
145     ret = link(template, lockfile);
146     if (ret == -1)
147       {
148 	if (errno == EEXIST)
149 	  {
150 	    sleep(ttt);
151 	    ttt+= 5;
152 	  }
153 	else
154 	  {
155 	    perror("try_lock: link");
156 	    return -1;
157 	  }
158       }
159   } while (ret < 0 && ttt < 65);
160 
161   return ret;
162 }
163 
create_lockfile(char * lockfile)164 static int create_lockfile(char * lockfile)
165 {
166   int    fd;
167   int    ret;
168   char * template = malloc(strlen(lockfile) + 7);
169 
170   assert(template != NULL);
171 
172   strcpy(template, lockfile);
173   strcat(template, "XXXXXX");
174 
175   fd = mkstemp(template);
176   if (fd == -1)
177     {
178       free(template);
179       perror("create_lockfile: mkstemp");
180       return -1;
181     }
182 
183   if (write_pid(fd) == -1)
184     {
185       unlink(template);
186       free(template);
187       return -1;
188     }
189 
190   ret = try_lock(template, lockfile);
191   if (ret < 0)
192     {
193       unlink(template);
194       free(template);
195       return -1;
196     }
197 
198   unlink(template);
199   free(template);
200   return 0;
201 }
202 
acquire_lock(char * file)203 int acquire_lock(char * file)
204 {
205   char * lockfile = malloc(strlen(file) + 6);
206 
207   assert(lockfile != NULL);
208 
209   strcpy(lockfile, file);
210   strcat(lockfile, ".lock");
211 
212   add_this_lockfile(lockfile);
213   if (create_lockfile(lockfile) < 0)
214     {
215       free_this_lockfile(lockfile);
216       free(lockfile);
217       return -1;
218     }
219 
220 
221   free(lockfile);
222   return 0;
223 }
224 
valid_lock(char * file)225 int valid_lock(char * file)
226 {
227   struct stat buf;
228   int ret;
229   char * lockfile = malloc(strlen(file) + 6);
230 
231   assert(lockfile != NULL);
232 
233   strcpy(lockfile, file);
234   strcat(lockfile, ".lock");
235 
236   ret = stat(lockfile, &buf);
237   if (ret < 0)
238     {
239       free(lockfile);
240       return -1;
241     }
242 
243   ret = test_lock(lockfile);
244   return ret;
245 }
246 
remove_lock(char * file)247 int remove_lock(char * file)
248 {
249   int ret;
250   char * lockfile = malloc(strlen(file) + 6);
251 
252   assert(lockfile != NULL);
253 
254   strcpy(lockfile, file);
255   strcat(lockfile, ".lock");
256 
257   ret = unlink(lockfile);
258 
259   if (ret < 0 && errno != ENOENT)
260     {
261       perror("remove_lock: unlink");
262       ret = -1;
263     }
264   else
265     {
266       free_this_lockfile(lockfile);
267       ret = 0;
268     }
269 
270   free(lockfile);
271   return ret;
272 }
273 
274 
275