1 /* $Id$ */
2 /*
3 * Copyright (c) 1990-1996 Sam Leffler
4 * Copyright (c) 1991-1996 Silicon Graphics, Inc.
5 * HylaFAX is a trademark of Silicon Graphics
6 *
7 * Permission to use, copy, modify, distribute, and sell this software and
8 * its documentation for any purpose is hereby granted without fee, provided
9 * that (i) the above copyright notices and this permission notice appear in
10 * all copies of the software and related documentation, and (ii) the names of
11 * Sam Leffler and Silicon Graphics may not be used in any advertising or
12 * publicity relating to the software without the specific, prior written
13 * permission of Sam Leffler and Silicon Graphics.
14 *
15 * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
17 * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
18 *
19 * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
20 * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
21 * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
22 * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF
23 * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
24 * OF THIS SOFTWARE.
25 */
26 #include "UUCPLock.h"
27 #include "faxApp.h"
28 #include "Sys.h"
29 #include "config.h"
30
31 #include <sys/param.h>
32 #include <sys/types.h>
33 #include <errno.h>
34 #ifdef HAS_MKDEV
35 extern "C" {
36 #include <sys/mkdev.h>
37 }
38 #endif
39 #include <pwd.h>
40
41 /*
42 * UUCP Device Locking Support.
43 *
44 * NB: There is some implicit understanding of sizeof (pid_t).
45 */
46
47 /*
48 * Lock files with ascii contents (System V style).
49 */
50 class AsciiUUCPLock : public UUCPLock {
51 private:
52 fxStr data; // data to write record in lock file
53
54 void setPID(pid_t);
55 bool writeData(int fd);
56 bool readData(int fd, pid_t& pid);
57 public:
58 AsciiUUCPLock(const fxStr&, mode_t);
59 ~AsciiUUCPLock();
60 };
61
62 /*
63 * Lock files with binary contents (BSD style).
64 */
65 class BinaryUUCPLock : public UUCPLock {
66 private:
67 pid_t data; // data to write record in lock file
68
69 void setPID(pid_t);
70 bool writeData(int fd);
71 bool readData(int fd, pid_t& pid);
72 public:
73 BinaryUUCPLock(const fxStr&, mode_t);
74 ~BinaryUUCPLock();
75 };
76
77 UUCPLock*
newLock(const char * type,const fxStr & dir,const fxStr & device,mode_t mode)78 UUCPLock::newLock(const char* type,
79 const fxStr& dir, const fxStr& device, mode_t mode)
80 {
81 fxStr pathname(dir);
82
83 if (type[0] == '+') { // SVR4-style lockfile names
84 #if defined(HAS_MKDEV) || (defined(major) && defined(minor))
85 /*
86 * SVR4-style lockfile names are of the form LK.xxx.yyy.zzz
87 * where xxx is the major device of the filesystem on which
88 * the device resides and yyy and zzz are the major & minor
89 * numbers of the locked device. This format is used if the
90 * lockfile type is specified with a leading '+'; e.g.
91 * ``+ascii'' or ``+binary''.
92 */
93 struct stat sb;
94 Sys::stat(device, sb);
95 pathname.append(fxStr::format("/LK.%03d.%03d.%03d",
96 major(sb.st_dev), major(sb.st_rdev), minor(sb.st_rdev)));
97 type++;
98 #else
99 faxApp::fatal("No support for SVR4-style UUCP lockfiles");
100 #endif
101 } else { // everybody else's lockfile names
102 u_int l = device.nextR(device.length(), '/');
103 pathname.append("/LCK.." | device.token(l, '/'));
104 if (type[0] == '-') { // SCO-style lockfile names
105 /*
106 * Some systems (e.g. SCO) uses upper case letters on modem
107 * control devices, but require that the locking be done on
108 * the lower case device. If the lock file type is specified
109 * as ``-ascii'' or ``-binary'', etc. then we convert the
110 * generated pathname to reflect this convention.
111 */
112 pathname.lowercase(dir.length()+6);
113 type++;
114 }
115 }
116 if (streq(type, "ascii"))
117 return new AsciiUUCPLock(pathname, mode);
118 else if (streq(type, "binary"))
119 return new BinaryUUCPLock(pathname, mode);
120 else
121 faxApp::fatal("Unknown UUCP lock file type \"%s\"", type);
122 return (NULL);
123 }
124
UUCPLock(const fxStr & pathname,mode_t m)125 UUCPLock::UUCPLock(const fxStr& pathname, mode_t m) : file(pathname)
126 {
127 mode = m;
128 locked = false;
129
130 setupIDs();
131 }
132
~UUCPLock()133 UUCPLock::~UUCPLock()
134 {
135 unlock();
136 }
137
138 uid_t UUCPLock::UUCPuid = (uid_t) -1;
139 gid_t UUCPLock::UUCPgid = (gid_t) -1;
140
141 void
setupIDs()142 UUCPLock::setupIDs()
143 {
144 if (UUCPuid == (uid_t) -1) {
145 const passwd *pwd = getpwnam("uucp");
146 if (!pwd)
147 faxApp::fatal("Can not deduce identity of UUCP");
148 UUCPuid = pwd->pw_uid;
149 UUCPgid = pwd->pw_gid;
150 endpwent(); // paranoia
151 }
152 }
getUUCPUid()153 uid_t UUCPLock::getUUCPUid() { setupIDs(); return UUCPuid; }
getUUCPGid()154 gid_t UUCPLock::getUUCPGid() { setupIDs(); return UUCPgid; }
155
156 time_t UUCPLock::lockTimeout = UUCP_LCKTIMEOUT;
setLockTimeout(time_t t)157 void UUCPLock::setLockTimeout(time_t t) { lockTimeout = t; }
158
159 /*
160 * Create a lock file.
161 */
162 bool
create()163 UUCPLock::create()
164 {
165 /*
166 * We create a separate file and link it to
167 * the destination to avoid a race condition.
168 */
169 fxStr templ = file.head(file.nextR(file.length(), '/'));
170 templ.append("/TM.faxXXXXXX");
171 char* buff = strcpy(new char[templ.length() + 1], templ);
172 int fd = Sys::mkstemp(buff);
173 if (fd >= 0) {
174 writeData(fd);
175 #if HAS_FCHMOD
176 fchmod(fd, mode);
177 #else
178 Sys::chmod(buff, mode);
179 #endif
180 #if HAS_FCHOWN
181 (void) fchown(fd, UUCPuid, UUCPgid);
182 #else
183 Sys::chown(buff, UUCPuid, UUCPgid);
184 #endif
185 Sys::close(fd);
186
187 locked = (Sys::link(buff, file) == 0);
188 Sys::unlink(buff);
189 }
190 delete [] buff;
191 return (locked);
192 }
193
194 /*
195 * Check if the lock file is
196 * newer than the specified age.
197 */
198 bool
isNewer(time_t age)199 UUCPLock::isNewer(time_t age)
200 {
201 struct stat sb;
202 return (Sys::stat(file, sb) != 0 ? false : Sys::now() - sb.st_mtime < age);
203 }
204
205 /*
206 * Create a lock file. If one already exists, the create
207 * time is checked for older than the age time (atime).
208 * If it is older, an attempt is made to unlink it and
209 * create a new one.
210 */
211 bool
lock()212 UUCPLock::lock()
213 {
214 if (locked)
215 return (false);
216 uid_t ouid = geteuid();
217 seteuid(0); // need to be root
218 bool ok = create();
219 if (!ok)
220 ok = check() && create();
221 seteuid(ouid);
222 return (ok);
223 }
224
225 /*
226 * Unlock the device.
227 */
228 void
unlock()229 UUCPLock::unlock()
230 {
231 if (locked) {
232 uid_t ouid = geteuid();
233 seteuid(0); // need to be root
234 Sys::unlink(file);
235 seteuid(ouid);
236 locked = false;
237 }
238 }
239
240 /*
241 * Set a particular owner process
242 * pid for an already locked device.
243 */
244 bool
setOwner(pid_t pid)245 UUCPLock::setOwner(pid_t pid)
246 {
247 bool ok = false;
248 if (locked) {
249 uid_t ouid = geteuid();
250 seteuid(0); // need to be root
251 int fd = Sys::open(file, O_WRONLY);
252 if (fd != -1) {
253 if (pid)
254 setPID(pid);
255 ok = writeData(fd);
256 Sys::close(fd);
257 }
258 seteuid(ouid);
259 }
260 return (ok);
261 }
262
263 /*
264 * Check if the owning process exists.
265 */
266 bool
ownerExists(int fd)267 UUCPLock::ownerExists(int fd)
268 {
269 pid_t pid;
270 return (readData(fd, pid) && (kill(pid, 0) == 0 || errno != ESRCH));
271 }
272
273 /*
274 * Check to see if the lock exists and is still active.
275 * Locks are automatically expired after
276 * UUCPLock::lockTimeout seconds if the process owner
277 * is no longer around.
278 */
279 bool
check()280 UUCPLock::check()
281 {
282 int fd = Sys::open(file, O_RDONLY);
283 if (fd != -1) {
284 if (lockTimeout > 0) {
285 if (isNewer(lockTimeout) || ownerExists(fd)) {
286 Sys::close(fd);
287 return (false);
288 }
289 Sys::close(fd);
290 logInfo("Purge stale UUCP lock %s", (const char*) file);
291 return (Sys::unlink(file) == 0);
292 } else {
293 Sys::close(fd);
294 return (false);
295 }
296 }
297 return (true);
298 }
299
300 /*
301 * ASCII lock file interface.
302 */
AsciiUUCPLock(const fxStr & path,mode_t m)303 AsciiUUCPLock::AsciiUUCPLock(const fxStr& path, mode_t m)
304 : UUCPLock(path, m)
305 , data(UUCP_PIDDIGITS+2)
306 { setPID(getpid()); }
~AsciiUUCPLock()307 AsciiUUCPLock::~AsciiUUCPLock() {}
308
309 void
setPID(pid_t pid)310 AsciiUUCPLock::setPID(pid_t pid)
311 {
312 // XXX should this be %d or %ld? depends on pid_t
313 data = fxStr::format("%*d\n", UUCP_PIDDIGITS, pid);
314 }
315
316 bool
writeData(int fd)317 AsciiUUCPLock::writeData(int fd)
318 {
319 return (Sys::write(fd, data, UUCP_PIDDIGITS+1) == (UUCP_PIDDIGITS+1));
320 }
321
322 bool
readData(int fd,pid_t & pid)323 AsciiUUCPLock::readData(int fd, pid_t& pid)
324 {
325 char buf[UUCP_PIDDIGITS+1];
326 if (Sys::read(fd, buf, UUCP_PIDDIGITS) == UUCP_PIDDIGITS) {
327 buf[UUCP_PIDDIGITS] = '\0';
328 pid = atol(buf); // NB: assumes pid_t is <= 32-bits
329 return (true);
330 } else
331 return (false);
332 }
333
334 /*
335 * Binary lock file interface.
336 */
BinaryUUCPLock(const fxStr & path,mode_t m)337 BinaryUUCPLock::BinaryUUCPLock(const fxStr& path, mode_t m)
338 : UUCPLock(path, m)
339 { setPID(getpid()); }
~BinaryUUCPLock()340 BinaryUUCPLock::~BinaryUUCPLock() {}
341
342 void
setPID(pid_t pid)343 BinaryUUCPLock::setPID(pid_t pid)
344 {
345 data = pid; // binary pid of lock holder
346 }
347
348 bool
writeData(int fd)349 BinaryUUCPLock::writeData(int fd)
350 {
351 return (Sys::write(fd, (char*) &data, sizeof (data)) == sizeof (data));
352 }
353
354 bool
readData(int fd,pid_t & pid)355 BinaryUUCPLock::readData(int fd, pid_t& pid)
356 {
357 pid_t data;
358 if (Sys::read(fd, (char*) &data, sizeof (data)) == sizeof (data)) {
359 pid = data;
360 return (true);
361 } else
362 return (false);
363 }
364