]> mj.ucw.cz Git - umpf.git/blob - lock.c
fix many little bugs, release 0.1
[umpf.git] / lock.c
1 #define _GNU_SOURCE
2
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <string.h>
6
7 #include <unistd.h>
8 #include <fcntl.h>
9 #include <errno.h>
10 #include <sys/stat.h>
11 #include <time.h>
12
13 #include "umpf.h"
14
15 #define LOCK_MAX_TRIES 3
16 gid_t rgid, egid, sgid;
17
18 void
19 save_gids(void)
20 {
21         getresgid(&rgid, &egid, &sgid); 
22 }
23
24 static void
25 drop_gid(void)
26 {
27         setresgid(rgid, rgid, egid);
28 }
29
30 static void
31 raise_gid(void)
32 {
33         setresgid(rgid, sgid, sgid);
34 }
35
36 static void
37 random_sleep(unsigned int about, unsigned int offset)
38 {
39         int myrand;
40
41         if (about == 0 || offset > about)
42                 die("random sleep: Bad input data: %d +- %d", about, offset);
43
44         myrand = random() % offset * (random()%2?1:-1);
45
46         usleep(about * 1000000 + myrand * 500000);
47 }
48
49 char*
50 cat(char* l, char* r)
51 {
52         char* res = xmalloc(strlen(l) + strlen (r) + 1);
53         strcpy(res, l);
54         strcat(res, r);
55         
56         return res;     
57 }
58
59 static int
60 dot_lock(char* path)
61 {
62         int i;
63         int fd;
64         int res = -1;
65         char* lockfile = cat(path, ".lock");
66
67         raise_gid();
68         for (i = 0; i < LOCK_MAX_TRIES; i++){
69                 struct stat buf;
70
71                 if ((fd = open(lockfile, O_WRONLY | O_EXCL | O_CREAT, 0)) 
72                         >= 0) {
73                         close(fd);
74                         res = 0;
75                         break;
76                 }
77
78                 if (errno != EEXIST)
79                         break;
80                 /* deal with old locks */
81                 stat(lockfile, &buf);
82                 if ((time(NULL) - buf.st_mtime) > 3600)
83                         unlink(lockfile);               
84                 random_sleep(1, 1);
85         }
86         drop_gid();
87
88         free(lockfile);
89         return res;
90 }
91
92 static void
93 dot_unlock(char* path)
94 {
95         char* lockfile = cat(path, ".lock");
96
97         raise_gid();
98         unlink(lockfile);
99         drop_gid();
100         free(lockfile);
101 }
102
103 static int
104 do_open_mailbox(char* path, int is_default_mailbox)
105 {
106         int fd;
107
108         /* attempt to dot_lock first */
109         if (dot_lock(path))
110                 return -1;
111
112         /* if OK, try to open the file:
113                 either we are saving to default mailbox (no problem here)
114                 or we are saving somewhere else (no need to be privileged)
115         */
116         int perms = S_IRUSR | S_IWUSR;
117         if (is_default_mailbox)
118                 perms |=  (S_IRGRP | S_IWGRP);
119
120         mode_t oldmask = umask(0);
121         fd = open(path, O_RDWR | O_CREAT, perms);
122         umask(oldmask);
123         if (fd < 0){
124                 dot_unlock(path);
125                 return -1;
126         }       
127
128         /* fcntl then */
129         struct flock mb_lock;
130         memset(&mb_lock, 0, sizeof(struct flock));
131         mb_lock.l_type = F_WRLCK;
132         mb_lock.l_whence = SEEK_SET;
133
134         if (fcntl(fd, F_GETLK, &mb_lock) < 0){
135                 close(fd);
136                 dot_unlock(path);
137                 return -1;
138         }
139
140         return fd; 
141 }
142
143 static void
144 do_close_mailbox(int fd, char* path)
145 {
146         struct flock mb_lock;
147         memset(&mb_lock, 0, sizeof(struct flock));
148         mb_lock.l_type = F_UNLCK; 
149         fcntl(fd, F_SETLK, &mb_lock);
150         close(fd);
151         dot_unlock(path);
152 }
153
154 void
155 close_mailbox(int fd, char* path, int is_default_mailbox)
156 {
157         if (is_default_mailbox)
158                 raise_gid();
159         do_close_mailbox(fd, path);
160         if (is_default_mailbox)
161                 drop_gid();
162 }
163
164 int
165 open_mailbox(char* path, int is_default_mailbox)
166 {
167         int fd;
168
169         if (is_default_mailbox)
170                 raise_gid();
171         fd = do_open_mailbox(path, is_default_mailbox);
172         if (is_default_mailbox)
173                 drop_gid();
174
175         return fd;
176 }