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