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