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