maillock.c revision 1219:f89f56c2d9ac
1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License").  You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22
23/*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
24/*	  All Rights Reserved  	*/
25
26/*
27 * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
28 * Use is subject to license terms.
29 */
30
31#pragma ident	"%Z%%M%	%I%	%E% SMI"
32/*LINTLIBRARY*/
33
34#include "c_synonyms.h"
35#include "maillock.h"
36#include <sys/types.h>
37#include <fcntl.h>
38#include <stdio.h>
39#include <string.h>
40#include <unistd.h>
41#include <stdlib.h>
42#include <utime.h>
43
44#include <sys/stat.h>
45
46static	char	*lockext = ".lock";	/* Lock suffix for mailname */
47static	char	curlock[PATHSIZE];	/* Last used name of lock */
48static	int	locked;			/* To note that we locked it */
49static	time_t	locktime;		/* time lock file was touched */
50static time_t	lock1(char *, char *);
51
52/*
53 * Lock the specified mail file by setting the file mailfile.lock.
54 * We must, of course, be careful to remove the lock file by a call
55 * to unlock before we stop.  The algorithm used here is to see if
56 * the lock exists, and if it does, to check its modify time.  If it
57 * is older than 5 minutes, we assume error and set our own file.
58 * Otherwise, we wait for 5 seconds and try again.
59 */
60
61/*ARGSUSED*/
62int
63maillock(char *user, int retrycnt)
64{
65	time_t t;
66	struct stat sbuf;
67	int statfailed;
68	char locktmp[PATHSIZE];	/* Usable lock temporary */
69	char file[PATHSIZE];
70
71	if (locked)
72		return (0);
73	(void) strcpy(file, MAILDIR);
74	(void) strcat(file, user);
75	(void) strcpy(curlock, file);
76	(void) strcat(curlock, lockext);
77	(void) strcpy(locktmp, file);
78	(void) strcat(locktmp, "XXXXXX");
79	(void) mktemp(locktmp);
80	(void) remove(locktmp);
81	statfailed = 0;
82	for (;;) {
83		t = lock1(locktmp, curlock);
84		if (t == (time_t)0) {
85			locked = 1;
86			locktime = time(0);
87			return (0);
88		}
89		if (stat(curlock, &sbuf) < 0) {
90			if (statfailed++ > 5)
91				return (-1);
92			(void) sleep(5);
93			continue;
94		}
95		statfailed = 0;
96
97		/*
98		 * Compare the time of the temp file with the time
99		 * of the lock file, rather than with the current
100		 * time of day, since the files may reside on
101		 * another machine whose time of day differs from
102		 * ours.  If the lock file is less than 5 minutes
103		 * old, keep trying.
104		 */
105		if (t < sbuf.st_ctime + 300) {
106			(void) sleep(5);
107			continue;
108		}
109		(void) remove(curlock);
110	}
111}
112
113/*
114 * Remove the mail lock, and note that we no longer
115 * have it locked.
116 */
117void
118mailunlock(void)
119{
120	(void) remove(curlock);
121	locked = 0;
122}
123
124/*
125 * Attempt to set the lock by creating the temporary file,
126 * then doing a link/unlink.  If it succeeds, return 0,
127 * else return a guess of the current time on the machine
128 * holding the file.
129 */
130static time_t
131lock1(char tempfile[], char name[])
132{
133	int fd;
134	struct stat sbuf;
135
136	fd = open(tempfile, O_RDWR|O_CREAT|O_EXCL, 0600);
137	if (fd < 0)
138		return (time(0));
139	(void) fstat(fd, &sbuf);
140	/*
141	 * Write the string "0" into the lock file to give us some
142	 * interoperability with SVR4 mailers.  SVR4 mailers expect
143	 * a process ID to be written into the lock file and then
144	 * use kill() to see if the process is alive or not.  We write
145	 * 0 into it so that SVR4 mailers will always think our lock file
146	 * is valid.
147	 */
148	(void) write(fd, "0", 2);
149	(void) close(fd);
150	if (link(tempfile, name) < 0) {
151		(void) remove(tempfile);
152		return (sbuf.st_ctime);
153	}
154	(void) remove(tempfile);
155	return ((time_t)0);
156}
157
158/*
159 * Update the change time on the lock file so
160 * others will know we're still using it.
161 */
162void
163touchlock(void)
164{
165	struct stat sbuf;
166	time_t t;
167	struct utimbuf tp;
168
169	if (!locked)
170		return;
171
172	/* if it hasn't been at least 3 minutes, don't bother */
173	if (time(&t) < locktime + 180)
174		return;
175	locktime = t;
176
177	if (stat(curlock, &sbuf) < 0)
178		return;
179	/*
180	 * Don't actually change the times, we just want the
181	 * side effect that utime causes st_ctime to be set
182	 * to the current time.
183	 */
184	tp.actime = sbuf.st_atime;
185	tp.modtime = sbuf.st_mtime;
186	(void) utime(curlock, &tp);
187}
188