/**************************************************************** * * * Copyright 2005, 2011 Fidelity Information Services, Inc * * * * This source code contains the intellectual property * * of its copyright holder(s), and is made available * * under a license. If you do not know the terms of * * the license, please stop and do not read further. * * * ****************************************************************/ #include "mdef.h" #include #include #include "errno.h" #include "gtm_ipc.h" #include "gtm_stdlib.h" #include "gtm_string.h" #include "gtm_stat.h" #include "gtm_fcntl.h" #include "error.h" #include "send_msg.h" #include "gtmio.h" #include "iosp.h" #include "gdsroot.h" #include "v15_gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "v15_gdsbt.h" #include "gdsfhead.h" #include "v15_gdsfhead.h" #include "gdsblk.h" #include "gtm_c_stack_trace.h" #include "eintr_wrappers.h" #include "eintr_wrapper_semop.h" #include "mu_all_version_standalone.h" #ifdef __MVS__ #include "gtm_zos_io.h" #endif static int ftok_ids[FTOK_ID_CNT] = {1, 43}; error_def(ERR_MUSTANDALONE); error_def(ERR_DBOPNERR); error_def(ERR_SYSCALL); error_def(ERR_TEXT); ZOS_ONLY(error_def(ERR_BADTAG);) /* Aquire semaphores that on on all V4.x releases are the access control semaphores. In pre V4.2 releases they were based on an FTOK of the database name with an ID of '1'. In V4.2 and later, they are based on the FTOK of the database name with an ID of '43'. Since we do not know which flavor of database we are dealing with, we must create and acquire both flavors of semaphore and hold them for the duration of the phase 2 run. But just holding these semaphore is not sufficient to guarrantee standalone access. We also must attempt to attach to the shared memory for the segment. If it is found, standalone access is not achieved. Early V4 versions (prior to V4.2) created the shared memory with the same FTOK id as the semaphore. Later versions would have had the key of the created private section in the file-header. Use both approaches and fail our attempt if either succeeds. */ void mu_all_version_get_standalone(char_ptr_t db_fn, sem_info *sem_inf) { int i, rc, save_errno, fd; struct sembuf sop[4]; int shmid; ZOS_ONLY(int realfiletag;) v15_sgmnt_data v15_csd; /* Both semaphores must have value 0 and then all will be incremented to completely lockout any other potential users until we are done */ sop[0].sem_num = 0; sop[0].sem_op = 0; /* First check all semaphore have 0 value */ sop[1].sem_num = 1; sop[1].sem_op = 0; sop[2].sem_num = 0; sop[2].sem_op = 1; /* Increment all semaphores */ sop[3].sem_num = 1; sop[3].sem_op = 1; sop[0].sem_flg = sop[1].sem_flg = sop[2].sem_flg = sop[3].sem_flg = SEM_UNDO | IPC_NOWAIT; memset(sem_inf, 0, (SIZEOF(sem_inf) * FTOK_ID_CNT)); /* Zero all fields so we know what to clean up */ for (i = 0; FTOK_ID_CNT > i; ++i) { /* Once through for both ftok key'd semaphores */ sem_inf[i].ftok_key = FTOK(db_fn, ftok_ids[i]); if (-1 == sem_inf[i].ftok_key) { save_errno = errno; mu_all_version_release_standalone(sem_inf); rts_error(VARLSTCNT(8) ERR_SYSCALL, 5, RTS_ERROR_LITERAL("ftok()"), CALLFROM, save_errno); } sem_inf[i].sem_id = semget(sem_inf[i].ftok_key, 3, RWDALL | IPC_CREAT | IPC_EXCL); if (-1 == sem_inf[i].sem_id) { save_errno = errno; mu_all_version_release_standalone(sem_inf); /* Different platforms seem to make different checks in different order here so if the semaphore exists but is locked, on some platforms we get EAGAIN, if the semaphore exists but has fewer semaphores in the set that we are requesting we get EINVAL, but if all that is ok and the semaphore just already exists, we get EEXIST which is all we wanted to check anyway.. */ if (EEXIST == save_errno || EAGAIN == save_errno || EINVAL == save_errno) /* Semaphore already exists and/or is locked-- likely rundown needed */ rts_error(VARLSTCNT(4) MAKE_MSG_TYPE(ERR_MUSTANDALONE, ERROR), 2, RTS_ERROR_TEXT(db_fn)); else rts_error(VARLSTCNT(8) ERR_SYSCALL, 5, RTS_ERROR_LITERAL("semget()"), CALLFROM, save_errno); } SEMOP(sem_inf[i].sem_id, sop, 4, rc, NO_WAIT); if (-1 == rc) { save_errno = errno; mu_all_version_release_standalone(sem_inf); if (EAGAIN == save_errno) rts_error(VARLSTCNT(4) MAKE_MSG_TYPE(ERR_MUSTANDALONE, ERROR), 2, RTS_ERROR_TEXT(db_fn)); else rts_error(VARLSTCNT(8) ERR_SYSCALL, 5, RTS_ERROR_LITERAL("semop()"), CALLFROM, save_errno); } } /* First try to access shared memory based on the database FTOK id. Only need to do the ftok returned by the first FTOK done above as it was the only method where the shared memory and semaphore had the same key. Detailed description: The first semaphore (for project id 1) is for early V4 versions. In these versions, the database had the same shmid as the semaphore did. Given that we can try to attach to it and if it is found, we assume we do not have a standalone lock. The other option is this is a later database so we open it up and pull the shmid field out of the file-header (it was in the same place in all versions that had the shmid). Whatever that field is (regardless of version), if we are able to attach to shared memory, then we consider standalone a failure. This is not a 100% valid check but it is good enough for the few times this will actually be run (upgrade, downgrade and dbcertify). */ shmid = shmget(sem_inf[0].ftok_key, 0, RWDALL); if (-1 == shmid) { /* That failed, second check is if shmid stored in file-header (if any) exists */ fd = OPEN(db_fn, O_RDONLY); if (FD_INVALID == fd) { save_errno = errno; mu_all_version_release_standalone(sem_inf); rts_error(VARLSTCNT(5) ERR_DBOPNERR, 2, RTS_ERROR_TEXT(db_fn), save_errno); } # ifdef __MVS__ if (-1 == gtm_zos_tag_to_policy(fd, TAG_BINARY, &realfiletag)) TAG_POLICY_GTM_PUTMSG(db_fn, errno, realfiletag, TAG_BINARY); # endif LSEEKREAD(fd, 0, &v15_csd, SIZEOF(v15_csd), rc); if (0 != rc) { mu_all_version_release_standalone(sem_inf); rts_error(VARLSTCNT(8) ERR_SYSCALL, 5, RTS_ERROR_LITERAL("LSEEKREAD()"), CALLFROM, rc); } CLOSEFILE_RESET(fd, rc); /* resets "fd" to FD_INVALID */ if (0 != v15_csd.shmid && INVALID_SHMID != v15_csd.shmid) shmid = shmget(v15_csd.shmid, 0, RWDALL); } if (-1 != shmid) { mu_all_version_release_standalone(sem_inf); rts_error(VARLSTCNT(4) MAKE_MSG_TYPE(ERR_MUSTANDALONE, ERROR), 2, RTS_ERROR_TEXT(db_fn)); } } /* The input array (filled out by mu_all_version_get_standalone()) should tell us which resources we specifically allocated so we can then free those resources (and no other). */ void mu_all_version_release_standalone(sem_info *sem_inf) { int i, rc, save_errno; /* Note that we ignore most errors in this routine as we may get called with the alleged semaphores in just about any state. */ for (i = 0; 2 > i; ++i) { /* release/delete any held semaphores in this set */ if (sem_inf[i].sem_id && -1 != sem_inf[i].sem_id) { rc = semctl(sem_inf[i].sem_id, 0, IPC_RMID); if (-1 == rc && EIDRM != errno) { /* Don't care if semaphore already removed */ save_errno = errno; send_msg(VARLSTCNT(8) ERR_SYSCALL, 5, RTS_ERROR_LITERAL("semctl(remid)"), CALLFROM, save_errno); } sem_inf[i].sem_id = 0; /* Clear so we don't repeat if redriven */ } } }