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 #pragma ident "%Z%%M% %I% %E% SMI"
31
32 #include "maillock.h"
33 #include <sys/types.h>
34 #include <fcntl.h>
35 #include <stdio.h>
36 #include <string.h>
37 #include <unistd.h>
38 #include <stdlib.h>
39 #include <utime.h>
40
41 #include <sys/stat.h>
42
43 static char *lockext = ".lock"; /* Lock suffix for mailname */
44 static char curlock[PATHSIZE]; /* Last used name of lock */
45 static int locked; /* To note that we locked it */
46 static time_t locktime; /* time lock file was touched */
47 static time_t lock1(char *, char *);
48
49 /*
50 * Lock the specified mail file by setting the file mailfile.lock.
51 * We must, of course, be careful to remove the lock file by a call
52 * to unlock before we stop. The algorithm used here is to see if
53 * the lock exists, and if it does, to check its modify time. If it
54 * is older than 5 minutes, we assume error and set our own file.
55 * Otherwise, we wait for 5 seconds and try again.
56 */
57
58 /*ARGSUSED*/
59 int
maillock(char * user,int retrycnt)60 maillock(char *user, int retrycnt)
61 {
62 time_t t;
63 struct stat sbuf;
64 int statfailed;
65 char locktmp[PATHSIZE]; /* Usable lock temporary */
66 char file[PATHSIZE];
67
68 if (locked)
69 return (0);
70 (void) strcpy(file, MAILDIR);
71 (void) strcat(file, user);
72 (void) strcpy(curlock, file);
73 (void) strcat(curlock, lockext);
74 (void) strcpy(locktmp, file);
75 (void) strcat(locktmp, "XXXXXX");
76 (void) mktemp(locktmp);
77 (void) remove(locktmp);
78 statfailed = 0;
79 for (;;) {
80 t = lock1(locktmp, curlock);
81 if (t == (time_t)0) {
82 locked = 1;
83 locktime = time(0);
84 return (0);
85 }
86 if (stat(curlock, &sbuf) < 0) {
87 if (statfailed++ > 5)
88 return (-1);
89 (void) sleep(5);
90 continue;
91 }
92 statfailed = 0;
93
94 /*
95 * Compare the time of the temp file with the time
96 * of the lock file, rather than with the current
97 * time of day, since the files may reside on
98 * another machine whose time of day differs from
99 * ours. If the lock file is less than 5 minutes
100 * old, keep trying.
101 */
102 if (t < sbuf.st_ctime + 300) {
103 (void) sleep(5);
104 continue;
105 }
106 (void) remove(curlock);
107 }
108 }
109
110 /*
111 * Remove the mail lock, and note that we no longer
112 * have it locked.
113 */
114 void
mailunlock(void)115 mailunlock(void)
116 {
117 (void) remove(curlock);
118 locked = 0;
119 }
120
121 /*
122 * Attempt to set the lock by creating the temporary file,
123 * then doing a link/unlink. If it succeeds, return 0,
124 * else return a guess of the current time on the machine
125 * holding the file.
126 */
127 static time_t
lock1(char tempfile[],char name[])128 lock1(char tempfile[], char name[])
129 {
130 int fd;
131 struct stat sbuf;
132
133 fd = open(tempfile, O_RDWR|O_CREAT|O_EXCL, 0600);
134 if (fd < 0)
135 return (time(0));
136 (void) fstat(fd, &sbuf);
137 /*
138 * Write the string "0" into the lock file to give us some
139 * interoperability with SVR4 mailers. SVR4 mailers expect
140 * a process ID to be written into the lock file and then
141 * use kill() to see if the process is alive or not. We write
142 * 0 into it so that SVR4 mailers will always think our lock file
143 * is valid.
144 */
145 (void) write(fd, "0", 2);
146 (void) close(fd);
147 if (link(tempfile, name) < 0) {
148 (void) remove(tempfile);
149 return (sbuf.st_ctime);
150 }
151 (void) remove(tempfile);
152 return ((time_t)0);
153 }
154
155 /*
156 * Update the change time on the lock file so
157 * others will know we're still using it.
158 */
159 void
touchlock(void)160 touchlock(void)
161 {
162 struct stat sbuf;
163 time_t t;
164 struct utimbuf tp;
165
166 if (!locked)
167 return;
168
169 /* if it hasn't been at least 3 minutes, don't bother */
170 if (time(&t) < locktime + 180)
171 return;
172 locktime = t;
173
174 if (stat(curlock, &sbuf) < 0)
175 return;
176 /*
177 * Don't actually change the times, we just want the
178 * side effect that utime causes st_ctime to be set
179 * to the current time.
180 */
181 tp.actime = sbuf.st_atime;
182 tp.modtime = sbuf.st_mtime;
183 (void) utime(curlock, &tp);
184 }
185