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