Creating transaction locking scheme based on fcntl file locking
[platform/upstream/rpm.git] / lib / rpmlock.c
1
2 #include "system.h"
3
4 #include <sys/types.h>
5 #include <sys/stat.h>
6 #include <unistd.h>
7 #include <fcntl.h>
8 #include <string.h>
9
10 #include <rpmlib.h>
11
12 #include "rpmts.h"
13
14 #include "rpmlock.h"
15
16 /* Internal interface */
17
18 #define RPMLOCK_FILE "/var/lib/rpm/transaction.lock"
19
20 enum {
21         RPMLOCK_READ   = 1 << 0,
22         RPMLOCK_WRITE  = 1 << 1,
23         RPMLOCK_WAIT   = 1 << 2,
24 };
25
26 typedef struct {
27         int fd;
28         int openmode;
29 } rpmlock;
30
31 static rpmlock *rpmlock_new(const char *rootdir)
32 {
33         rpmlock *lock = (rpmlock *)malloc(sizeof(rpmlock));
34         if (lock) {
35                 mode_t oldmask = umask(022);
36                 char *path = (char *)malloc(strlen(rootdir)+
37                                             strlen(RPMLOCK_FILE));
38                 if (!path) {
39                         free(lock);
40                         return NULL;
41                 }
42                 sprintf(path, "%s/%s", rootdir, RPMLOCK_FILE);
43                 lock->fd = open(RPMLOCK_FILE, O_RDWR|O_CREAT, 0644);
44                 umask(oldmask);
45                 if (lock->fd == -1) {
46                         lock->fd = open(RPMLOCK_FILE, O_RDONLY);
47                         if (lock->fd == -1) {
48                                 free(lock);
49                                 lock = NULL;
50                         } else {
51                                 lock->openmode = RPMLOCK_READ;
52                         }
53                 } else {
54                         lock->openmode = RPMLOCK_WRITE | RPMLOCK_READ;
55                 }
56         }
57         return lock;
58 }
59
60 static void rpmlock_free(rpmlock *lock)
61 {
62         if (lock) {
63                 close(lock->fd);
64                 free(lock);
65         }
66 }
67
68 static int rpmlock_acquire(rpmlock *lock, int mode)
69 {
70         int res = 0;
71         if (lock && (mode & lock->openmode)) {
72                 struct flock info;
73                 int cmd;
74                 if (mode & RPMLOCK_WAIT)
75                         cmd = F_SETLKW;
76                 else
77                         cmd = F_SETLK;
78                 if (mode & RPMLOCK_READ)
79                         info.l_type = F_RDLCK;
80                 else
81                         info.l_type = F_WRLCK;
82                 info.l_whence = SEEK_SET;
83                 info.l_start = 0;
84                 info.l_len = 0;
85                 if (fcntl(lock->fd, cmd, &info) != -1)
86                         res = 1;
87         }
88         return res;
89 }
90
91 static void rpmlock_release(rpmlock *lock)
92 {
93         if (lock) {
94                 struct flock info;
95                 info.l_type = F_UNLCK;
96                 info.l_whence = SEEK_SET;
97                 info.l_start = 0;
98                 info.l_len = 0;
99                 fcntl(lock->fd, F_SETLK, &info);
100         }
101 }
102
103
104 /* External interface */
105
106 void *rpmtsAcquireLock(rpmts ts)
107 {
108         const char *rootDir = rpmtsRootDir(ts);
109         rpmlock *lock;
110         if (!rootDir)
111                 rootDir = "/";
112         lock = rpmlock_new(rootDir);
113         if (!lock) {
114                 rpmMessage(RPMMESS_ERROR, _("can't create transaction lock\n"));
115         } else if (!rpmlock_acquire(lock, RPMLOCK_WRITE)) {
116                 if (lock->openmode & RPMLOCK_WRITE)
117                         rpmMessage(RPMMESS_WARNING,
118                                    _("waiting for transaction lock\n"));
119                 if (!rpmlock_acquire(lock, RPMLOCK_WRITE|RPMLOCK_WAIT)) {
120                         rpmMessage(RPMMESS_ERROR,
121                                    _("can't create transaction lock\n"));
122                         rpmlock_free(lock);
123                         lock = NULL;
124                 }
125         }
126         return lock;
127 }
128
129 void rpmtsFreeLock(void *lock)
130 {
131         rpmlock_release((rpmlock *)lock); /* Not really needed here. */
132         rpmlock_free((rpmlock *)lock);
133 }
134
135