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