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"
55struct updatearray {
56	mutex_t		updatenode[MAXHASH];
57};
58typedef 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 */
66static struct updatearray	*shmupdatearray;
67static int	lockfile;
68
69bool_t
70init_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
105bool_t
106init_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
224suc_code
225lock_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
280suc_code
281unlock_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 */
314bool_t
315is_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 */
343int
344try_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