xref: /titanic_44/usr/src/cmd/ypcmd/shared/lockmap.c (revision b7f45089ccbe01bab3d7c7377b49d80d2ae18a69)
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 2003 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <unistd.h>
30 #include <syslog.h>
31 #include <sys/mman.h>
32 #include <thread.h>
33 #include <synch.h>
34 #include <ndbm.h>
35 #include "../ypsym.h"
36 #include "../ypdefs.h"
37 
38 /*
39  *  These routines provide mutual exclusion between ypserv and ypxfr.
40  *  Mutual exclusion is needed so that ypxfr doesn't try to rename
41  *  dbm files while ypserv is trying to open them.  After ypserv has
42  *  opened a dbm file, it is safe to rename it because ypserv still
43  *  has access to the file through its file descriptor.
44  */
45 
46 #define	LOCKFILE "/var/run/yp_maplock"
47 struct lockarray {
48 	mutex_t		locknode[MAXHASH];
49 };
50 typedef struct lockarray lockarray;
51 
52 /*
53  * Cross-process robust mutex locks.
54  * Provide synchronization between YP processes
55  * by implementing an exclusive locking mechanism
56  * via a memory-mapped file.
57  */
58 static struct lockarray	*shmlockarray;
59 static int	lockfile;
60 
61 int
62 hash(char *s)
63 {
64 	int n = 0;
65 	int i;
66 
67 	for (i = 1; *s; i += 10, s++) {
68 		n += i * (*s);
69 	}
70 	n %= MAXHASH;
71 	return (n);
72 }
73 
74 bool
75 init_locks_mem()
76 {
77 	int iiter, rc;
78 	int ebusy_cnt = 0;
79 
80 	/*
81 	 * Initialize cross-process locks in memory-mapped file.
82 	 */
83 	for (iiter = 0; iiter < MAXHASH; iiter++) {
84 		if (rc = mutex_init(&(shmlockarray->locknode[iiter]),
85 		    USYNC_PROCESS_ROBUST, 0)) {
86 			if (rc == EBUSY) {
87 				ebusy_cnt++;
88 			} else {
89 				syslog(LOG_ERR,
90 				    "init_locks_mem():mutex_init():error=%d",
91 				    rc);
92 				return (FALSE);
93 			}
94 		}
95 	}
96 
97 	/*
98 	 * EBUSY for all locks OK, it means another process
99 	 * has already initialized locks.
100 	 */
101 	if ((ebusy_cnt > 0) && (ebusy_cnt != MAXHASH)) {
102 		syslog(LOG_ERR,
103 		    "%s inconsistent. Remove and restart NIS (YP).", LOCKFILE);
104 		return (FALSE);
105 	}
106 	return (TRUE);
107 }
108 
109 bool
110 init_lock_map()
111 {
112 	char buff[ sizeof (lockarray) ];
113 	int write_cnt, lf_size;
114 	struct stat fdata;
115 
116 	/*
117 	 * Locking file initialization algorithm, with recovery mechanism.
118 	 * This mechanism has been devised to ensure proper creation
119 	 * of a memory-mapped lock file containing mutexes for robust,
120 	 * inter-process communication.
121 	 * File name is /var/run/yp_maplock (LOCKFILE).  It might or might
122 	 * not exist.
123 	 *
124 	 * Algorithm:
125 	 * Try to open the file. If file doesn't exist, or size is too small,
126 	 * create/rewrite the file, m-map it into memory and initialize the
127 	 * mutexes in it.
128 	 * If file exists and size is at least large enough, assume it's a
129 	 * good file, and m-map the lock structure directly to it.
130 	 *
131 	 * Recovery from inconsistent state is easy - simply delete the file
132 	 * and restart NIS (YP).
133 	 */
134 
135 	lockfile = open(LOCKFILE, O_RDWR|O_CREAT, 0600);
136 	if (lockfile != -1) {
137 		if (lockf(lockfile, F_LOCK, 0) == 0) {
138 			if (fstat(lockfile, &fdata) == 0) {
139 				lf_size = fdata.st_size;
140 				if (lf_size < sizeof (lockarray)) {
141 					bzero(buff, sizeof (buff));
142 					if ((write_cnt = write(lockfile, buff,
143 					    sizeof (buff)) != sizeof (buff))) {
144 						if (write_cnt < 0) {
145 							syslog(LOG_ERR,
146 						    "write(%s) => errno=%d",
147 							    LOCKFILE, errno);
148 						} else {
149 							syslog(LOG_ERR,
150 		    "write(%s) => %d!=%d: wrong number of bytes written.",
151 							    LOCKFILE,
152 							    write_cnt,
153 							    sizeof (buff));
154 						}
155 						lockf(lockfile, F_ULOCK, 0);
156 						close(lockfile);
157 						return (FALSE);
158 					}
159 				}
160 			} else {
161 				syslog(LOG_ERR,
162 				    "fstat(%s) => errno=%d", LOCKFILE, errno);
163 				lockf(lockfile, F_ULOCK, 0);
164 				close(lockfile);
165 				return (FALSE);
166 			}
167 		} else {
168 			syslog(LOG_ERR,
169 			    "lockf(%s,F_LOCK) => errno=%d", LOCKFILE, errno);
170 			close(lockfile);
171 			return (FALSE);
172 		}
173 	} else {
174 		syslog(LOG_ERR,
175 		    "open(%s) => errno=%d", LOCKFILE, errno);
176 		return (FALSE);
177 	}
178 
179 	/*
180 	 * File exists with correct size, is open, and we're holding
181 	 * the file lock.
182 	 */
183 	shmlockarray = (lockarray *)mmap((caddr_t) 0, sizeof (lockarray),
184 	    PROT_READ | PROT_WRITE, MAP_SHARED, lockfile, 0);
185 	if (shmlockarray == MAP_FAILED) {
186 		syslog(LOG_ERR, "mmap(%s) => errno=%d", LOCKFILE, errno);
187 		lockf(lockfile, F_ULOCK, 0);
188 		close(lockfile);
189 		return (FALSE);
190 	}
191 
192 	/*
193 	 * If we wrote zeroes to the file, we also need to initialize
194 	 * the mutex locks.
195 	 */
196 	if (lf_size < sizeof (lockarray)) {
197 		if (init_locks_mem() == FALSE) {
198 			lockf(lockfile, F_ULOCK, 0);
199 			close(lockfile);
200 			if (remove(LOCKFILE) != 0) {
201 				syslog(LOG_ERR,
202 			    "remove(%s) => errno=%d: Please delete file.",
203 				    LOCKFILE, errno);
204 			}
205 			return (FALSE);
206 		}
207 	}
208 
209 	if (lockf(lockfile, F_ULOCK, 0) != 0) {
210 		syslog(LOG_ERR,
211 		    "lockf(%s,F_ULOCK) => errno=%d",
212 		    LOCKFILE, errno);
213 		close(lockfile);
214 		return (FALSE);
215 	}
216 
217 	if (close(lockfile) == 0) {
218 		return (TRUE);
219 	} else {
220 		syslog(LOG_ERR,
221 		    "close(%s) => errno=%d", LOCKFILE, errno);
222 		return (FALSE);
223 	}
224 }
225 
226 /*
227  * FUNCTION : 	lock_map()
228  *
229  * DESCRIPTION: Front end to the lock routine taking map name as argument.
230  *
231  * GIVEN :	Map name.
232  *
233  * RETURNS :	Same as lock_core
234  */
235 int
236 lock_map(char *mapname)
237 {
238 	int hashval;
239 
240 	hashval = hash(mapname);
241 
242 	return( lock_core(hashval));
243 }
244 
245 /*
246  * FUNCTION : 	lock_core()
247  *
248  * DESCRIPTION: The core map locking function
249  *
250  * GIVEN :	Map hash value
251  *
252  * RETURNS :	0 = Failure
253  *		1 = Success
254  */
255 int
256 lock_core(int hashval)
257 {
258 	int rc;
259 
260 	/*
261 	 *Robust, cross-process lock implementation
262 	 */
263 	rc = mutex_lock(&(shmlockarray->locknode[hashval]));
264 	while (rc != 0) {
265 		switch (rc) {
266 		case EOWNERDEAD:
267 			/*
268 			 * Previows lock owner died, resetting lock
269 			 * to recover from error.
270 			 */
271 			rc = mutex_init(&(shmlockarray->locknode[hashval]),
272 			    USYNC_PROCESS_ROBUST, 0);
273 			if (rc != 0) {
274 				syslog(LOG_ERR,
275 				    "mutex_init(): error=%d", rc);
276 				return (0);
277 			}
278 			rc = mutex_unlock(&(shmlockarray->locknode[hashval]));
279 			if (rc != 0) {
280 				syslog(LOG_ERR,
281 				    "mutex_unlock(): error=%d", rc);
282 				return (0);
283 			}
284 			break;
285 		default:
286 			/*
287 			 * Unrecoverable problem - nothing to do
288 			 * but exit YP and delete lock file.
289 			 */
290 			syslog(LOG_ERR,
291 			    "mutex_lock(): error=%d", rc);
292 			syslog(LOG_ERR,
293 			    "Please restart NIS (ypstop/ypstart).");
294 			if (remove(LOCKFILE) != 0) {
295 				syslog(LOG_ERR,
296 			    "remove(%s) => errno=%d: Please delete file.",
297 				    LOCKFILE, errno);
298 			}
299 			return (0);
300 		}
301 		rc = mutex_lock(&(shmlockarray->locknode[hashval]));
302 	}
303 
304 	/* Success */
305 	return (1);
306 }
307 
308 
309 /*
310  * FUNCTION : 	unlock_map()
311  *
312  * DESCRIPTION: Front end to the unlock routine taking map name as argument.
313  *
314  * GIVEN :	Map name.
315  *
316  * RETURNS :	Same as unlock_core
317  */
318 int
319 unlock_map(char *mapname)
320 {
321 	int hashval;
322 
323 	hashval = hash(mapname);
324 
325 	return( unlock_core( hashval ));
326 }
327 
328 /*
329  * FUNCTION : 	unlock_core()
330  *
331  * DESCRIPTION: The core map locking function
332  *
333  * GIVEN :	Map hash value
334  *
335  * RETURNS :	0 = Failure
336  *		1 = Success
337  */
338 int
339 unlock_core(int hashval)
340 {
341 	int rc;
342 
343 	rc = mutex_unlock(&(shmlockarray->locknode[hashval]));
344 	if (rc != 0) {
345 		syslog(LOG_ERR,
346 		    "mutex_unlock(): error=%d", rc);
347 		syslog(LOG_ERR,
348 		    "Please restart NIS (ypstop/ypstart).");
349 		if (remove(LOCKFILE) != 0) {
350 			syslog(LOG_ERR,
351 			    "remove(%s) => errno=%d: Please delete file.",
352 			    LOCKFILE, errno);
353 		}
354 		return (0);
355 	}
356 
357 	/* Success */
358 	return (1);
359 }
360