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
init_update_locks_mem()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
init_update_lock_map()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
lock_map_update(map_ctrl * map)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
unlock_map_update(map_ctrl * map)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
is_map_updating(map_ctrl * map)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
try_lock_map_update(map_ctrl * map)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