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