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