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