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