1 
2 #include "system.h"
3 
4 #include <errno.h>
5 #include <fcntl.h>
6 
7 #include <rpm/rpmlog.h>
8 #include <rpm/rpmfileutil.h>
9 
10 #include "lib/rpmlock.h"
11 
12 #include "debug.h"
13 
14 /* Internal interface */
15 
16 struct rpmlock_s {
17     int fd;
18     int openmode;
19     char *path;
20     char *descr;
21     int fdrefs;
22 };
23 
rpmlock_new(const char * lock_path,const char * descr)24 static rpmlock rpmlock_new(const char *lock_path, const char *descr)
25 {
26     rpmlock lock = (rpmlock) malloc(sizeof(*lock));
27 
28     if (lock != NULL) {
29 	mode_t oldmask = umask(022);
30 	lock->fd = open(lock_path, O_RDWR|O_CREAT, 0644);
31 	(void) umask(oldmask);
32 
33 	if (lock->fd == -1) {
34 	    if (errno == EACCES)
35 		lock->fd = open(lock_path, O_RDONLY);
36 	    if (lock->fd == -1) {
37 		free(lock);
38 		lock = NULL;
39 	    } else {
40 		lock->openmode = RPMLOCK_READ;
41 	    }
42 	} else {
43 	    lock->openmode = RPMLOCK_WRITE | RPMLOCK_READ;
44 	}
45 	if (lock) {
46 	    lock->path = xstrdup(lock_path);
47 	    lock->descr = xstrdup(descr);
48 	    lock->fdrefs = 1;
49 	}
50     }
51     return lock;
52 }
53 
rpmlock_free(rpmlock lock)54 static void rpmlock_free(rpmlock lock)
55 {
56     if (--lock->fdrefs == 0) {
57 	free(lock->path);
58 	free(lock->descr);
59 	(void) close(lock->fd);
60 	free(lock);
61     }
62 }
63 
rpmlock_acquire(rpmlock lock,int mode)64 static int rpmlock_acquire(rpmlock lock, int mode)
65 {
66     int res = 0;
67 
68     if (!(mode & lock->openmode))
69 	return res;
70 
71     if (lock->fdrefs > 1) {
72 	res = 1;
73     } else {
74 	struct flock info;
75 	int cmd;
76 	if (mode & RPMLOCK_WAIT)
77 	    cmd = F_SETLKW;
78 	else
79 	    cmd = F_SETLK;
80 	if (mode & RPMLOCK_READ)
81 	    info.l_type = F_RDLCK;
82 	else
83 	    info.l_type = F_WRLCK;
84 	info.l_whence = SEEK_SET;
85 	info.l_start = 0;
86 	info.l_len = 0;
87 	info.l_pid = 0;
88 	if (fcntl(lock->fd, cmd, &info) != -1)
89 	    res = 1;
90     }
91 
92     lock->fdrefs += res;
93 
94     return res;
95 }
96 
rpmlock_release(rpmlock lock)97 static void rpmlock_release(rpmlock lock)
98 {
99     /* if not locked then we must not release */
100     if (lock->fdrefs <= 1)
101 	return;
102 
103     if (--lock->fdrefs == 1) {
104 	struct flock info;
105 	info.l_type = F_UNLCK;
106 	info.l_whence = SEEK_SET;
107 	info.l_start = 0;
108 	info.l_len = 0;
109 	info.l_pid = 0;
110 	(void) fcntl(lock->fd, F_SETLK, &info);
111      }
112 }
113 
114 
115 /* External interface */
rpmlockNew(const char * lock_path,const char * descr)116 rpmlock rpmlockNew(const char *lock_path, const char *descr)
117 {
118     rpmlock lock = rpmlock_new(lock_path, descr);
119     if (!lock) {
120 	rpmlog(RPMLOG_ERR, _("can't create %s lock on %s (%s)\n"),
121 		descr, lock_path, strerror(errno));
122     }
123     return lock;
124 }
125 
rpmlockAcquire(rpmlock lock)126 int rpmlockAcquire(rpmlock lock)
127 {
128     int locked = 0; /* assume failure */
129     int myerrno = errno;
130     int maywait = isatty(STDIN_FILENO); /* dont wait within scriptlets */
131     errno = myerrno;
132 
133     if (lock) {
134 	locked = rpmlock_acquire(lock, RPMLOCK_WRITE);
135 	if (!locked && (lock->openmode & RPMLOCK_WRITE) && maywait) {
136 	    rpmlog(RPMLOG_WARNING, _("waiting for %s lock on %s\n"),
137 		    lock->descr, lock->path);
138 	    locked = rpmlock_acquire(lock, (RPMLOCK_WRITE|RPMLOCK_WAIT));
139 	}
140 	if (!locked) {
141 	    rpmlog(RPMLOG_ERR, _("can't create %s lock on %s (%s)\n"),
142 		   lock->descr, lock->path, strerror(errno));
143 	}
144     }
145     return locked;
146 }
147 
rpmlockRelease(rpmlock lock)148 void rpmlockRelease(rpmlock lock)
149 {
150     if (lock)
151 	rpmlock_release(lock);
152 }
153 
rpmlockNewAcquire(const char * lock_path,const char * descr)154 rpmlock rpmlockNewAcquire(const char *lock_path, const char *descr)
155 {
156     rpmlock lock = rpmlockNew(lock_path, descr);
157     if (!rpmlockAcquire(lock))
158 	lock = rpmlockFree(lock);
159     return lock;
160 }
161 
rpmlockFree(rpmlock lock)162 rpmlock rpmlockFree(rpmlock lock)
163 {
164     if (lock) {
165 	rpmlock_release(lock);
166 	rpmlock_free(lock);
167     }
168     return NULL;
169 }
170 
171 
172