xref: /illumos-gate/usr/src/lib/libnisdb/yptol/lock_update.c (revision 581cede61ac9c14d8d4ea452562a567189eead78)
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 /*
30  * DESCRIPTION: Contains code supporting the 'update in progress' flag. This is
31  *		a near copy of lock flag code (in
32  *		usr/src/cmd/ypcmd/shared/lockmp.c) If we implement a clean
33  *		version	of the locking code this file will probably disappear.
34  *
35  *		These locks are held while a map is being updated from the
36  *		DIT. They prevent a second update being started while this is
37  *		in progress. This is independant from the `lockmap` mechanism
38  *		which protects maps, generally for a much shorter period,
39  *		while their control structures are modified.
40  */
41 
42 #include <unistd.h>
43 #include <syslog.h>
44 #include <sys/mman.h>
45 #include <thread.h>
46 #include <synch.h>
47 #include <ndbm.h>
48 #include <strings.h>
49 #include "ypsym.h"
50 #include "shim.h"
51 #include "yptol.h"
52 #include "../ldap_util.h"
53 
54 #define	LOCKFILE "/var/run/yp_mapupdate"
55 struct updatearray {
56 	mutex_t		updatenode[MAXHASH];
57 };
58 typedef struct updatearray updatearray;
59 
60 /*
61  * Cross-process robust mutex locks.
62  * Provide synchronization between YP processes
63  * by implementing an exclusive locking mechanism
64  * via a memory-mapped file.
65  */
66 static struct updatearray	*shmupdatearray;
67 static int	lockfile;
68 
69 bool_t
70 init_update_locks_mem()
71 {
72 	int iiter, rc;
73 	int ebusy_cnt = 0;
74 
75 	/*
76 	 * Initialize cross-process locks in memory-mapped file.
77 	 */
78 	for (iiter = 0; iiter < MAXHASH; iiter++) {
79 		if (rc = mutex_init(&(shmupdatearray->updatenode[iiter]),
80 		    USYNC_PROCESS | LOCK_ROBUST, 0)) {
81 			if (rc == EBUSY) {
82 				ebusy_cnt++;
83 			} else {
84 				logmsg(MSG_NOTIMECHECK, LOG_ERR,
85 					"init_update_locks_mem():mutex_init():"
86 					"error=%d", rc);
87 				return (FALSE);
88 			}
89 		}
90 	}
91 
92 	/*
93 	 * EBUSY for all locks OK, it means another process
94 	 * has already initialized locks.
95 	 */
96 	if ((ebusy_cnt > 0) && (ebusy_cnt != MAXHASH)) {
97 		logmsg(MSG_NOTIMECHECK, LOG_ERR,
98 		"%s inconsistent. Remove this file and restart NIS (YP)",
99 								LOCKFILE);
100 		return (FALSE);
101 	}
102 	return (TRUE);
103 }
104 
105 bool_t
106 init_update_lock_map()
107 {
108 	char buff[ sizeof (updatearray) ];
109 	int write_cnt, lf_size;
110 	struct stat fdata;
111 
112 	/*
113 	 * Locking file initialization algorithm, with recovery mechanism.
114 	 * This mechanism has been devised to ensure proper creation
115 	 * of a memory-mapped lock file containing mutexes for robust,
116 	 * inter-process communication.
117 	 * File name is /var/run/yp_mapupate (LOCKFILE).  It might or might
118 	 * not exist.
119 	 *
120 	 * Algorithm:
121 	 * Try to open the file. If file doesn't exist, or size is too small,
122 	 * create/rewrite the file, m-map it into memory and initialize the
123 	 * mutexes in it.
124 	 * If file exists and size is at least large enough, assume it's a
125 	 * good file, and m-map the lock structure directly to it.
126 	 *
127 	 * Recovery from inconsistent state is easy - simply delete the file
128 	 * and restart NIS (YP).
129 	 */
130 
131 	lockfile = open(LOCKFILE, O_RDWR|O_CREAT, 0600);
132 	if (lockfile != -1) {
133 		if (lockf(lockfile, F_LOCK, 0) == 0) {
134 			if (fstat(lockfile, &fdata) == 0) {
135 				lf_size = fdata.st_size;
136 				if (lf_size < sizeof (updatearray)) {
137 					bzero(buff, sizeof (buff));
138 					if ((write_cnt = write(lockfile, buff,
139 					    sizeof (buff)) != sizeof (buff))) {
140 						if (write_cnt < 0) {
141 							logmsg(MSG_NOTIMECHECK,
142 								LOG_ERR,
143 						"write(%s) => errno=%d",
144 							    LOCKFILE, errno);
145 						} else {
146 							logmsg(MSG_NOTIMECHECK,
147 								LOG_ERR,
148 		    "write(%s) => %d!=%d: wrong number of bytes written",
149 							    LOCKFILE,
150 							    write_cnt,
151 							    sizeof (buff));
152 						}
153 						lockf(lockfile, F_ULOCK, 0);
154 						close(lockfile);
155 						return (FALSE);
156 					}
157 				}
158 			} else {
159 				logmsg(MSG_NOTIMECHECK, LOG_ERR,
160 				    "fstat(%s) => errno=%d", LOCKFILE, errno);
161 				lockf(lockfile, F_ULOCK, 0);
162 				close(lockfile);
163 				return (FALSE);
164 			}
165 		} else {
166 			logmsg(MSG_NOTIMECHECK, LOG_ERR,
167 			    "lockf(%s,F_LOCK) => errno=%d", LOCKFILE, errno);
168 			close(lockfile);
169 			return (FALSE);
170 		}
171 	} else {
172 		logmsg(MSG_NOTIMECHECK, LOG_ERR,
173 			"open(%s) => errno=%d", LOCKFILE, errno);
174 		return (FALSE);
175 	}
176 
177 	/*
178 	 * File exists with correct size, is open, and we're holding
179 	 * the file lock.
180 	 */
181 	shmupdatearray = (updatearray *)mmap((caddr_t)0, sizeof (updatearray),
182 	    PROT_READ | PROT_WRITE, MAP_SHARED, lockfile, 0);
183 	if (shmupdatearray == MAP_FAILED) {
184 		logmsg(MSG_NOTIMECHECK, LOG_ERR,
185 				"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 (updatearray)) {
196 		if (init_update_locks_mem() == FALSE) {
197 			lockf(lockfile, F_ULOCK, 0);
198 			close(lockfile);
199 			if (remove(LOCKFILE) != 0) {
200 				logmsg(MSG_NOTIMECHECK, 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 		logmsg(MSG_NOTIMECHECK, LOG_ERR,
210 			"lockf(%s,F_ULOCK) => errno=%d", LOCKFILE, errno);
211 		close(lockfile);
212 		return (FALSE);
213 	}
214 
215 	if (close(lockfile) == 0) {
216 		return (TRUE);
217 	} else {
218 		logmsg(MSG_NOTIMECHECK, LOG_ERR,
219 				"close(%s) => errno=%d", LOCKFILE, errno);
220 		return (FALSE);
221 	}
222 }
223 
224 suc_code
225 lock_map_update(map_ctrl *map)
226 {
227 	int hashval = map->hash_val;
228 	int rc;
229 
230 	/*
231 	 * Robust, cross-process lock implementation
232 	 */
233 	rc = mutex_lock(&(shmupdatearray->updatenode[hashval]));
234 	while (rc != 0) {
235 		switch (rc) {
236 		case EOWNERDEAD:
237 			/*
238 			 * Previous lock owner died, resetting lock
239 			 * to recover from error.
240 			 */
241 			rc = mutex_consistent(
242 			    &(shmupdatearray->updatenode[hashval]));
243 			if (rc != 0) {
244 				logmsg(MSG_NOTIMECHECK, LOG_ERR,
245 					"mutex_consistent(): error=%d", rc);
246 				return (FAILURE);
247 			}
248 			rc = mutex_unlock(
249 			    &(shmupdatearray->updatenode[hashval]));
250 			if (rc != 0) {
251 				logmsg(MSG_NOTIMECHECK, LOG_ERR,
252 					"mutex_unlock(): error=%d", rc);
253 				return (FAILURE);
254 			}
255 			break;
256 		default:
257 			/*
258 			 * Unrecoverable problem - nothing to do
259 			 * but exit YP and delete lock file.
260 			 */
261 			logmsg(MSG_NOTIMECHECK, LOG_ERR,
262 						"mutex_lock(): error=%d", rc);
263 			logmsg(MSG_NOTIMECHECK, LOG_ERR,
264 					"Please restart NIS (ypstop/ypstart)");
265 			if (remove(LOCKFILE) != 0) {
266 				logmsg(MSG_NOTIMECHECK, LOG_ERR,
267 				"remove(%s) => errno=%d: Please delete file",
268 							LOCKFILE, errno);
269 			}
270 			return (FAILURE);
271 		}
272 		rc = mutex_lock(&(shmupdatearray->updatenode[hashval]));
273 	}
274 
275 	/* Success */
276 	return (SUCCESS);
277 }
278 
279 
280 suc_code
281 unlock_map_update(map_ctrl *map)
282 {
283 	int hashval = map->hash_val;
284 	int rc;
285 
286 	rc = mutex_unlock(&(shmupdatearray->updatenode[hashval]));
287 	if (rc != 0) {
288 		logmsg(MSG_NOTIMECHECK, LOG_ERR,
289 						"mutex_unlock(): error=%d", rc);
290 		logmsg(MSG_NOTIMECHECK, LOG_ERR,
291 					"Please restart NIS (ypstop/ypstart)");
292 		if (remove(LOCKFILE) != 0) {
293 			logmsg(MSG_NOTIMECHECK, LOG_ERR,
294 			    "remove(%s) => errno=%d: Please delete file",
295 			    LOCKFILE, errno);
296 		}
297 		return (FAILURE);
298 	}
299 
300 	/* Success */
301 	return (SUCCESS);
302 }
303 
304 /*
305  * FUNCTION :   is_map_updating()
306  *
307  * DESCRIPTION: Determines if a map is currently locked for update
308  *
309  * GIVEN :      Pointer to map_ctrl structure
310  *
311  * RETURNS :    TRUE = Map is locked
312  *              FALSE = Map is not locked
313  */
314 bool_t
315 is_map_updating(map_ctrl *map)
316 {
317 	int ret;
318 
319 	/* It appears not to be possible to just read a mutex. Try to lock it */
320 	ret = mutex_trylock(&(shmupdatearray->updatenode[map->hash_val]));
321 
322 	if (0 != ret) {
323 		/* Didn't get the lock ... was already locked */
324 		return (TRUE);
325 	}
326 
327 	/* Didn't need the lock so free it again */
328 	mutex_unlock(&(shmupdatearray->updatenode[map->hash_val]));
329 	return (FALSE);
330 }
331 
332 /*
333  * FUNCTION :	try_lock_map_update()
334  *
335  * DESCRIPTION: Tries to to lock a map for update.
336  *
337  * GIVEN :	Pointer to the map to lock
338  *
339  * RETURNS :	0 = The map is now locked
340  *		EBUSY = The map was already locked lock not obtained.
341  *		Other = There was an error
342  */
343 int
344 try_lock_map_update(map_ctrl *map)
345 {
346 	int hashval = map->hash_val;
347 	int rc;
348 
349 	/*
350 	 * Robust, cross-process lock implementation
351 	 *
352 	 * Keep trying until either lock is obtained or somebody else gets it.
353 	 */
354 	while (1) {
355 		rc = mutex_trylock(&(shmupdatearray->updatenode[hashval]));
356 
357 		switch (rc) {
358 
359 		case 0:
360 		case EBUSY:
361 			/* Either got it or somebody else has it */
362 			return (rc);
363 
364 		case EOWNERDEAD:
365 			/*
366 			 * Previous lock owner died, resetting lock
367 			 * to recover from error.
368 			 */
369 			rc = mutex_consistent(
370 			    &(shmupdatearray->updatenode[hashval]));
371 			if (rc != 0) {
372 				logmsg(MSG_NOTIMECHECK, LOG_ERR,
373 					"mutex_consistent(): error=%d", rc);
374 				return (rc);
375 			}
376 			rc = mutex_unlock(
377 			    &(shmupdatearray->updatenode[hashval]));
378 			if (rc != 0) {
379 				logmsg(MSG_NOTIMECHECK, LOG_ERR,
380 					"mutex_unlock(): error=%d", rc);
381 				return (rc);
382 			}
383 			break;
384 		default:
385 			/*
386 			 * Unrecoverable problem - nothing to do
387 			 * but exit YP and delete lock file.
388 			 */
389 			logmsg(MSG_NOTIMECHECK, LOG_ERR,
390 						"mutex_lock(): error=%d", rc);
391 			logmsg(MSG_NOTIMECHECK, LOG_ERR,
392 					"Please restart NIS (ypstop/ypstart)");
393 			if (remove(LOCKFILE) != 0) {
394 				logmsg(MSG_NOTIMECHECK, LOG_ERR,
395 				"remove(%s) => errno=%d: Please delete file",
396 							LOCKFILE, errno);
397 			}
398 			return (rc);
399 		}
400 	}
401 }
402