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