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