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
  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
 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
 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
 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
 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
 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 }