/**************************************************************** * * * Copyright 2001, 2012 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 #include "gtm_ipc.h" #include "gtm_fcntl.h" #include "gtm_unistd.h" #include "gtm_inet.h" #include "gtm_stdlib.h" #include "gtm_string.h" #include "gtm_sem.h" #include "gtm_stat.h" #include "gtm_stdio.h" #include "gtmio.h" #include "gdsroot.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "iosp.h" #include "mutex.h" #include "jnl.h" #include "repl_sem.h" #include "eintr_wrappers.h" #include "mu_rndwn_file.h" #include "repl_msg.h" #include "gtmsource.h" #include "gtmrecv.h" #include "gtm_logicals.h" #include "min_max.h" #include "util.h" #include "mu_rndwn_replpool.h" #include "mu_rndwn_all.h" #include "dbfilop.h" #include "ipcrmid.h" #include "mu_gv_cur_reg_init.h" #include "gtmmsg.h" #include "cliif.h" #include "mu_rndwn_repl_instance.h" #include "send_msg.h" #include "do_shmat.h" /* for do_shmat() prototype */ #include "shmpool.h" /* Needed for the shmpool structures */ #include "error.h" #ifdef GTM_SNAPSHOT #include "db_snapshot.h" #endif #define PRINT_AND_SEND_SHMREMOVED_MSG(MSGBUFF, FNAME_LEN, FNAME, SHMID) \ { \ gtm_putmsg(VARLSTCNT(9) ERR_TEXT, 2, LEN_AND_STR(MSGBUFF), ERR_SHMREMOVED, 3, SHMID, FNAME_LEN, FNAME); \ send_msg(VARLSTCNT(9) ERR_TEXT, 2, LEN_AND_STR(MSGBUFF), ERR_SHMREMOVED, 3, SHMID, FNAME_LEN, FNAME); \ } #define PRINT_AND_SEND_REPLPOOL_FAILURE_MSG(MSGBUFF, REPLPOOL_ID, SHMID) \ { \ int msgid; \ unsigned char ipcs_buff[MAX_IPCS_ID_BUF], *ipcs_ptr; \ \ ipcs_ptr = i2asc(ipcs_buff, SHMID); \ *ipcs_ptr = '\0'; \ msgid = (JNLPOOL_SEGMENT == REPLPOOL_ID->pool_type) ? ERR_MUJPOOLRNDWNFL : ERR_MURPOOLRNDWNFL; \ gtm_putmsg(VARLSTCNT(10) ERR_TEXT, 2, LEN_AND_STR(MSGBUFF), msgid, 4, LEN_AND_STR(ipcs_buff), \ LEN_AND_STR(REPLPOOL_ID->instfilename)); \ send_msg(VARLSTCNT(10) ERR_TEXT, 2, LEN_AND_STR(MSGBUFF), msgid, 4, LEN_AND_STR(ipcs_buff), \ LEN_AND_STR(REPLPOOL_ID->instfilename)); \ } #define PRINT_AND_SEND_DBRNDWN_FAILURE_MSG(MSGBUFF, FNAME, SHMID) \ { \ gtm_putmsg(VARLSTCNT(9) ERR_TEXT, 2, LEN_AND_STR(MSGBUFF), ERR_MUFILRNDWNFL2, 3, SHMID, LEN_AND_STR(FNAME)); \ send_msg(VARLSTCNT(9) ERR_TEXT, 2, LEN_AND_STR(MSGBUFF), ERR_MUFILRNDWNFL2, 3, SHMID, LEN_AND_STR(FNAME)); \ } GBLREF gd_region *gv_cur_region; LITREF char gtm_release_name[]; LITREF int4 gtm_release_name_len; error_def(ERR_DBFILERR); error_def(ERR_MUFILRNDWNFL2); error_def(ERR_MUFILRNDWNSUC); error_def(ERR_MUJPOOLRNDWNFL); error_def(ERR_MUJPOOLRNDWNSUC); error_def(ERR_MUNOTALLSEC); error_def(ERR_MURPOOLRNDWNFL); error_def(ERR_MURPOOLRNDWNSUC); error_def(ERR_SEMREMOVED); error_def(ERR_SHMREMOVED); error_def(ERR_SYSCALL); error_def(ERR_TEXT); STATICFNDCL boolean_t validate_db_shm_entry(shm_parms *parm_buff, char *fname, int *tmp_exit_status); STATICFNDCL boolean_t validate_replpool_shm_entry(shm_parms *parm_buff, replpool_id_ptr_t replpool_id, int *tmp_exit_status); STATICFNDCL shm_parms *get_shm_parm(char *entry); STATICFNDCL char *parse_shm_entry(char *entry, int which_field); STATICFNDCL void mu_rndwn_all_helper(shm_parms *parm_buff, char *fname, int *exit_status, int *tmp_exit_status); STATICDEF boolean_t mu_rndwn_all_helper_error = FALSE; /* This condition handler is necessary so the argumentless "mupip rundown" does not terminate in case of an error * while processing one ipc. Instead it moves on to the next ipc. The only exception is fatal errors (SEVERE) * where it might not be safe to continue processing so we transfer control to a higher level condition handler. */ CONDITION_HANDLER(mu_rndwn_all_helper_ch) { START_CH; mu_rndwn_all_helper_error = TRUE; PRN_ERROR; if (SEVERITY == SEVERE) { NEXTCH; } else UNWIND(NULL, NULL); } STATICFNDEF void mu_rndwn_all_helper(shm_parms *parm_buff, char *fname, int *exit_status, int *tmp_exit_status) { replpool_identifier replpool_id; boolean_t ret_status, jnlpool_sem_created; unsigned char ipcs_buff[MAX_IPCS_ID_BUF], *ipcs_ptr; ESTABLISH(mu_rndwn_all_helper_ch); if (validate_db_shm_entry(parm_buff, fname, tmp_exit_status)) { if (SS_NORMAL == *tmp_exit_status) { /* shm still exists */ mu_gv_cur_reg_init(); gv_cur_region->dyn.addr->fname_len = strlen(fname); STRNCPY_STR(gv_cur_region->dyn.addr->fname, fname, gv_cur_region->dyn.addr->fname_len); if (mu_rndwn_file(gv_cur_region, FALSE)) gtm_putmsg(VARLSTCNT(4) ERR_MUFILRNDWNSUC, 2, DB_LEN_STR(gv_cur_region)); else *exit_status = ERR_MUNOTALLSEC; mu_gv_cur_reg_free(); } else { /* shm has been cleaned up by "validate_db_shm_entry" so no need of any more cleanup here */ assert(ERR_SHMREMOVED == *tmp_exit_status); *tmp_exit_status = SS_NORMAL; /* reset tmp_exit_status for below logic to treat this as normal */ } } else if ((SS_NORMAL == *tmp_exit_status) && validate_replpool_shm_entry(parm_buff, (replpool_id_ptr_t)&replpool_id, tmp_exit_status)) { if (SS_NORMAL == *tmp_exit_status) { assert(JNLPOOL_SEGMENT == replpool_id.pool_type || RECVPOOL_SEGMENT == replpool_id.pool_type); ret_status = mu_rndwn_repl_instance(&replpool_id, TRUE, FALSE, &jnlpool_sem_created); ipcs_ptr = i2asc((uchar_ptr_t)ipcs_buff, parm_buff->shmid); *ipcs_ptr = '\0'; gtm_putmsg(VARLSTCNT(6) (JNLPOOL_SEGMENT == replpool_id.pool_type) ? (ret_status ? ERR_MUJPOOLRNDWNSUC : ERR_MUJPOOLRNDWNFL) : (ret_status ? ERR_MURPOOLRNDWNSUC : ERR_MURPOOLRNDWNFL), 4, LEN_AND_STR(ipcs_buff), LEN_AND_STR(replpool_id.instfilename)); if (!ret_status) *exit_status = ERR_MUNOTALLSEC; } else { /* shm has been cleaned up by "validate_replpool_shm_entry" so no need of any more cleanup here */ assert(ERR_SHMREMOVED == *tmp_exit_status); *tmp_exit_status = SS_NORMAL; /* reset tmp_exit_status for below logic to treat this as normal */ } } REVERT; } int mu_rndwn_all(void) { int save_errno, fname_len, exit_status = SS_NORMAL, shmid, tmp_exit_status; char entry[MAX_ENTRY_LEN]; FILE *pf; char *fname, *fgets_res; shm_parms *parm_buff; if (NULL == (pf = POPEN(IPCS_CMD_STR ,"r"))) { save_errno = errno; gtm_putmsg(VARLSTCNT(8) ERR_SYSCALL, 5, RTS_ERROR_LITERAL("POPEN()"), CALLFROM, save_errno); return ERR_MUNOTALLSEC; } fname = (char *)malloc(MAX_FN_LEN + 1); while (NULL != (FGETS(entry, SIZEOF(entry), pf, fgets_res)) && entry[0] != '\n') { tmp_exit_status = SS_NORMAL; parm_buff = get_shm_parm(entry); if (NULL == parm_buff) { exit_status = ERR_MUNOTALLSEC; continue; } mu_rndwn_all_helper(parm_buff, fname, &exit_status, &tmp_exit_status); if ((SS_NORMAL == exit_status) && (SS_NORMAL != tmp_exit_status)) exit_status = tmp_exit_status; if (mu_rndwn_all_helper_error) { /* Encountered a runtime error while processing this ipc. Make sure we return with * MUNOTALLSEC and reset this static variable before starting processing on next ipc. */ mu_rndwn_all_helper_error = FALSE; if (SS_NORMAL == exit_status) exit_status = ERR_MUNOTALLSEC; } if (NULL != parm_buff) free(parm_buff); } pclose(pf); free(fname); return exit_status; } /* Takes an entry from 'ipcs -m' and checks for its validity to be a GT.M db segment. * Returns TRUE if the shared memory segment is a valid GT.M db segment * (based on a check on some fields in the shared memory) else FALSE. * If the segment belongs to GT.M it returns the database file name by the second argument. * Sets exit_stat to ERR_MUNOTALLSEC if appropriate. */ boolean_t validate_db_shm_entry(shm_parms *parm_buff, char *fname, int *exit_stat) { boolean_t remove_shmid; file_control *fc; int fname_len, save_errno, status, shmid; node_local_ptr_t nl_addr; sm_uc_ptr_t start_addr; struct stat st_buff; struct shmid_ds shmstat; sgmnt_data tsd; unix_db_info *udi; char msgbuff[OUT_BUFF_SIZE]; if (NULL == parm_buff) return FALSE; /* check for the bare minimum size of the shared memory segment that we expect * (with no fileheader related information at hand) */ if (NODE_LOCAL_SPACE + SHMPOOL_SECTION_SIZE > parm_buff->sgmnt_siz) return FALSE; if (IPC_PRIVATE != parm_buff->key) return FALSE; shmid = parm_buff->shmid; /* we do not need to lock the shm for reading the rundown information as * the other rundowns (if any) can also be allowed to share reading the * same info concurrently. */ if (-1 == (sm_long_t)(start_addr = (sm_uc_ptr_t) do_shmat(shmid, 0, SHM_RND))) return FALSE; nl_addr = (node_local_ptr_t)start_addr; memcpy(fname, nl_addr->fname, MAX_FN_LEN + 1); fname[MAX_FN_LEN] = '\0'; /* make sure the fname is null terminated */ fname_len = STRLEN(fname); msgbuff[0] = '\0'; if (memcmp(nl_addr->label, GDS_LABEL, GDS_LABEL_SZ - 1)) { if (!memcmp(nl_addr->label, GDS_LABEL, GDS_LABEL_SZ - 3)) { util_out_print("Cannot rundown shmid = !UL for database !AD as it has format !AD " "but this mupip uses format !AD", TRUE, shmid, fname_len, fname, GDS_LABEL_SZ - 1, nl_addr->label, GDS_LABEL_SZ - 1, GDS_LABEL); *exit_stat = ERR_MUNOTALLSEC; } shmdt((void *)start_addr); return FALSE; } if (memcmp(nl_addr->now_running, gtm_release_name, gtm_release_name_len + 1)) { SNPRINTF(msgbuff, OUT_BUFF_SIZE, "Cannot rundown database %s. Attempt to access with version %s, " "while already using %s", fname, gtm_release_name, nl_addr->now_running); PRINT_AND_SEND_DBRNDWN_FAILURE_MSG(msgbuff, fname, shmid); *exit_stat = ERR_MUNOTALLSEC; shmdt((void *)start_addr); return FALSE; } if (-1 == shmctl(shmid, IPC_STAT, &shmstat)) { save_errno = errno; assert(FALSE);/* we were able to attach to this shmid before so should be able to get stats on it */ util_out_print("!AD -> Error with shmctl for shmid = !UL", TRUE, fname_len, fname, shmid); gtm_putmsg(VARLSTCNT(1) save_errno); *exit_stat = ERR_MUNOTALLSEC; shmdt((void *)start_addr); return FALSE; } remove_shmid = FALSE; /* Check if db filename reported in shared memory still exists. If not, clean this shared memory section * without even invoking "mu_rndwn_file" as that expects the db file to exist. Same case if shared memory * points back to a database whose file header does not have this shmid. */ if (-1 == Stat(fname, &st_buff)) { if (ENOENT == errno) { SNPRINTF(msgbuff, OUT_BUFF_SIZE, "File %s does not exist", fname); if (1 < shmstat.shm_nattch) { PRINT_AND_SEND_DBRNDWN_FAILURE_MSG(msgbuff, fname, shmid); *exit_stat = ERR_MUNOTALLSEC; shmdt((void *)start_addr); return FALSE; } remove_shmid = TRUE; } else { /* Stat errored out e.g. due to file permissions. Log that */ save_errno = errno; util_out_print("Cannot rundown shmid !UL for database file !AD as stat() on the file" " returned the following error", TRUE, shmid, fname_len, fname); gtm_putmsg(VARLSTCNT(1) save_errno); *exit_stat = ERR_MUNOTALLSEC; shmdt((void *)start_addr); return FALSE; } } else { mu_gv_cur_reg_init(); gv_cur_region->dyn.addr->fname_len = strlen(fname); STRNCPY_STR(gv_cur_region->dyn.addr->fname, fname, gv_cur_region->dyn.addr->fname_len); fc = gv_cur_region->dyn.addr->file_cntl; fc->op = FC_OPEN; status = dbfilop(fc); if (SS_NORMAL != status) { util_out_print("!AD -> Error with dbfilop for shmid = !UL", TRUE, fname_len, fname, shmid); gtm_putmsg(VARLSTCNT(5) status, 2, DB_LEN_STR(gv_cur_region), errno); *exit_stat = ERR_MUNOTALLSEC; shmdt((void *)start_addr); return FALSE; } udi = FILE_INFO(gv_cur_region); LSEEKREAD(udi->fd, 0, &tsd, SIZEOF(sgmnt_data), status); if (0 != status) { save_errno = errno; util_out_print("!AD -> Error with LSEEKREAD for shmid = !UL", TRUE, fname_len, fname, shmid); gtm_putmsg(VARLSTCNT(1) save_errno); *exit_stat = ERR_MUNOTALLSEC; shmdt((void *)start_addr); return FALSE; } mu_gv_cur_reg_free(); if (tsd.shmid != shmid) { SNPRINTF(msgbuff, OUT_BUFF_SIZE, "Shared memory ID (%d) in the DB file header does not match with the one" " reported by \"ipcs\" command (%d)", tsd.shmid, shmid); if (1 < shmstat.shm_nattch) { PRINT_AND_SEND_DBRNDWN_FAILURE_MSG(msgbuff, fname, shmid); *exit_stat = ERR_MUNOTALLSEC; shmdt((void *)start_addr); return FALSE; } remove_shmid = TRUE; } else if (tsd.gt_shm_ctime.ctime != shmstat.shm_ctime) { SNPRINTF(msgbuff, OUT_BUFF_SIZE, "Shared memory creation time in the DB file header does not match with" " the one reported by shmctl"); if (1 < shmstat.shm_nattch) { PRINT_AND_SEND_DBRNDWN_FAILURE_MSG(msgbuff, fname, shmid); *exit_stat = ERR_MUNOTALLSEC; shmdt((void *)start_addr); return FALSE; } remove_shmid = TRUE; } } shmdt((void *)start_addr); if (remove_shmid) { assert('\0' != msgbuff[0]); if (0 != shm_rmid(shmid)) { save_errno = errno; gtm_putmsg(VARLSTCNT(8) ERR_DBFILERR, 2, fname_len, fname, ERR_TEXT, 2, RTS_ERROR_TEXT("Error removing shared memory")); util_out_print("!AD -> Error removing shared memory for shmid = !UL", TRUE, fname_len, fname, shmid); gtm_putmsg(VARLSTCNT(1) save_errno); *exit_stat = ERR_MUNOTALLSEC; return FALSE; } PRINT_AND_SEND_SHMREMOVED_MSG(msgbuff, fname_len, fname, shmid); *exit_stat = ERR_SHMREMOVED; } else *exit_stat = SS_NORMAL; return TRUE; } /* Takes an entry from 'ipcs -am' and checks for its validity to be a GT.M replication segment. * Returns TRUE if the shared memory segment is a valid GT.M replication segment * (based on a check on some fields in the shared memory) else FALSE. * If the segment belongs to GT.M, it returns the replication id of the segment * by the second argument. * Sets exit_stat to ERR_MUNOTALLSEC if appropriate. */ boolean_t validate_replpool_shm_entry(shm_parms *parm_buff, replpool_id_ptr_t replpool_id, int *exit_stat) { boolean_t remove_shmid; int fd; repl_inst_hdr repl_instance; sm_uc_ptr_t start_addr; int save_errno, status, shmid; struct shmid_ds shmstat; char msgbuff[OUT_BUFF_SIZE], *instfilename; if (NULL == parm_buff) return FALSE; /* Check for the bare minimum size of the replic shared segment that we expect */ /* if (parm_buff->sgmnt_siz < (SIZEOF(replpool_identifier) + MIN(MIN_JNLPOOL_SIZE, MIN_RECVPOOL_SIZE))) */ if (parm_buff->sgmnt_siz < MIN(MIN_JNLPOOL_SIZE, MIN_RECVPOOL_SIZE)) return FALSE; if (IPC_PRIVATE != parm_buff->key) return FALSE; shmid = parm_buff->shmid; /* we do not need to lock the shm for reading the rundown information as * the other rundowns (if any) can also be allowed to share reading the * same info concurrently. */ if (-1 == (sm_long_t)(start_addr = (sm_uc_ptr_t) do_shmat(shmid, 0, SHM_RND))) return FALSE; memcpy((void *)replpool_id, (void *)start_addr, SIZEOF(replpool_identifier)); instfilename = replpool_id->instfilename; /* Even though we could be looking at a replication pool structure that has been created by an older version * or newer version of GT.M, the format of the "replpool_identifier" structure is expected to be the same * across all versions so we can safely dereference the "label" and "instfilename" fields in order to generate * user-friendly error messages. Asserts for the layout are in "mu_rndwn_repl_instance" (not here) with a * comment there as to why that location was chosen. */ if (memcmp(replpool_id->label, GDS_RPL_LABEL, GDS_LABEL_SZ - 1)) { if (!memcmp(replpool_id->label, GDS_RPL_LABEL, GDS_LABEL_SZ - 3)) { util_out_print("Cannot rundown replpool shmid = !UL as it has format !AD " "created by !AD but this mupip is version and uses format !AD", TRUE, shmid, GDS_LABEL_SZ - 1, replpool_id->label, LEN_AND_STR(replpool_id->now_running), gtm_release_name_len, gtm_release_name, GDS_LABEL_SZ - 1, GDS_RPL_LABEL); *exit_stat = ERR_MUNOTALLSEC; } shmdt((void *)start_addr); return FALSE; } assert(JNLPOOL_SEGMENT == replpool_id->pool_type || RECVPOOL_SEGMENT == replpool_id->pool_type); if(JNLPOOL_SEGMENT != replpool_id->pool_type && RECVPOOL_SEGMENT != replpool_id->pool_type) { shmdt((void *)start_addr); return FALSE; } if (-1 == shmctl(shmid, IPC_STAT, &shmstat)) { save_errno = errno; assert(FALSE);/* we were able to attach to this shmid before so should be able to get stats on it */ util_out_print("!AD -> Error with shmctl for shmid = !UL", TRUE, LEN_AND_STR(instfilename), shmid); gtm_putmsg(VARLSTCNT(1) save_errno); *exit_stat = ERR_MUNOTALLSEC; shmdt((void *)start_addr); return FALSE; } /* Check if instance filename reported in shared memory still exists. If not, clean this * shared memory section without even invoking "mu_rndwn_repl_instance" as that expects * the instance file to exist. Same case if shared memory points back to an instance file * whose file header does not have this shmid. */ OPENFILE(instfilename, O_RDONLY, fd); /* check if we can open it */ msgbuff[0] = '\0'; remove_shmid = FALSE; if (FD_INVALID == fd) { if (ENOENT == errno) { SNPRINTF(msgbuff, OUT_BUFF_SIZE, "File %s does not exist", instfilename); if (1 < shmstat.shm_nattch) { PRINT_AND_SEND_REPLPOOL_FAILURE_MSG(msgbuff, replpool_id, shmid); *exit_stat = ERR_MUNOTALLSEC; shmdt((void *)start_addr); return FALSE; } remove_shmid = TRUE; } else { /* open() errored out e.g. due to file permissions. Log that */ save_errno = errno; util_out_print("Cannot rundown replpool shmid !UL for instance file" " !AD as open() on the file returned the following error", TRUE, shmid, LEN_AND_STR(instfilename)); gtm_putmsg(VARLSTCNT(1) save_errno); *exit_stat = ERR_MUNOTALLSEC; shmdt((void *)start_addr); return FALSE; } } else { LSEEKREAD(fd, 0, &repl_instance, SIZEOF(repl_inst_hdr), status); if (0 != status) { save_errno = errno; util_out_print("!AD -> Error with LSEEKREAD for shmid = !UL", TRUE, LEN_AND_STR(instfilename), shmid); gtm_putmsg(VARLSTCNT(1) save_errno); *exit_stat = ERR_MUNOTALLSEC; shmdt((void *)start_addr); return FALSE; } if (repl_instance.jnlpool_shmid != shmid) { SNPRINTF(msgbuff, OUT_BUFF_SIZE, "Shared memory ID (%d) in the instance file header does not match with the" " one reported by \"ipcs\" command (%d)", repl_instance.jnlpool_shmid, shmid); if (1 < shmstat.shm_nattch) { PRINT_AND_SEND_REPLPOOL_FAILURE_MSG(msgbuff, replpool_id, shmid); *exit_stat = ERR_MUNOTALLSEC; shmdt((void *)start_addr); return FALSE; } remove_shmid = TRUE; } CLOSEFILE_RESET(fd, status); /* resets "fd" to FD_INVALID */ } shmdt((void *)start_addr); if (remove_shmid) { assert('\0' != msgbuff[0]); if (0 != shm_rmid(shmid)) { save_errno = errno; util_out_print("!AD -> Error removing shared memory for shmid = !UL", TRUE, LEN_AND_STR(instfilename), shmid); gtm_putmsg(VARLSTCNT(1) save_errno); *exit_stat = ERR_MUNOTALLSEC; return FALSE; } PRINT_AND_SEND_SHMREMOVED_MSG(msgbuff, STRLEN(instfilename), instfilename, shmid); *exit_stat = ERR_SHMREMOVED; } else *exit_stat = SS_NORMAL; return TRUE; } /* Gets all the required fields in shm_parms struct for a given shm entry */ shm_parms *get_shm_parm(char *entry) { char *parm; shm_parms *parm_buff; struct shmid_ds shm_buf; parm_buff = (shm_parms *)malloc(SIZEOF(shm_parms)); parm = parse_shm_entry(entry, SHMID); CONVERT_TO_NUM(shmid); parm = parse_shm_entry(entry, KEY); CONVERT_TO_NUM(key); /* get shm segment size directly from shmid_ds instead of parsing ipcs * output (thus avoiding the -a option for ipcs in mu_rndwn_all() */ if (-1 == shmctl(parm_buff->shmid, IPC_STAT, &shm_buf)) { free(parm_buff); return NULL; } parm_buff->sgmnt_siz = shm_buf.shm_segsz; return parm_buff; } /* Parses the output of 'IPCS_CMD_STR' command. Returns the value of the * specified field. */ /* NOTE : Even though the standard says that every column in the output * of 'ipcs' command should be separated by atleast one space, we have * observed a case where there is no space between the first (T) and * second (ID) fields on AIX under certain conditions. * The workaround is to always insert a blank space for all UNIX platforms * after the first (T field) character assuming the entry always starts with a * character describing the type of the ipc resource ('m' for shared memory). * On linux, the ipcs output starts with a KEY field (a hexadecimal number). * See the definition of IPCS_CMD_STR for this handling */ char *parse_shm_entry(char *entry, int which_field) { char *parm; int iter, indx1 = 0, indx2 = 0; for(iter = 1; iter < which_field; iter++) { while(entry[indx1] == ' ') indx1++; while(entry[indx1] && entry[indx1] != ' ') indx1++; } while(entry[indx1] == ' ') indx1++; if ('\0' == entry[indx1]) { assert(FALSE); return NULL; } parm = (char *)malloc(MAX_PARM_LEN); memset(parm, 0, MAX_PARM_LEN); while(entry[indx1] && entry[indx1] != ' ') parm[indx2++] = entry[indx1++]; parm[indx2] = '\0'; return parm; } int parse_sem_id(char *entry) { char *parm; int iter, indx1 = 0, indx2; while(entry[indx1] == ' ') indx1++; while(entry[indx1] && entry[indx1] != ' ') indx1++; while(entry[indx1] == ' ') indx1++; if ('\0' == entry[indx1]) { assert(FALSE); return -1; } indx2 = indx1; parm = &entry[indx1]; while(entry[indx2] && entry[indx2] != ' ') indx2++; entry[indx2] = '\0'; if (cli_is_dcm(parm)) return (int)STRTOUL(parm, NULL, 10); else if (cli_is_hex(parm + 2)) return (int)STRTOUL(parm, NULL, 16); else { assert(FALSE); return -1; } } int mu_rndwn_sem_all(void) { int save_errno, exit_status = SS_NORMAL, semid; char entry[MAX_ENTRY_LEN]; FILE *pf; char fname[MAX_FN_LEN + 1], *fgets_res; boolean_t rem_sem; shm_parms *parm_buff; if (NULL == (pf = POPEN(IPCS_SEM_CMD_STR ,"r"))) { save_errno = errno; gtm_putmsg(VARLSTCNT(8) ERR_SYSCALL, 5, RTS_ERROR_LITERAL("POPEN()"), CALLFROM, save_errno); return ERR_MUNOTALLSEC; } while (NULL != (FGETS(entry, SIZEOF(entry), pf, fgets_res)) && entry[0] != '\n') { if (-1 != (semid = parse_sem_id(entry))) { if (is_orphaned_gtm_semaphore(semid)) { if (-1 != semctl(semid, 0, IPC_RMID)) { gtm_putmsg(VARLSTCNT(3) ERR_SEMREMOVED, 1, semid); send_msg(VARLSTCNT(3) ERR_SEMREMOVED, 1, semid); } } } } pclose(pf); return exit_status; } boolean_t is_orphaned_gtm_semaphore(int semid) { int semno, semval; struct semid_ds semstat; union semun semarg; semarg.buf = &semstat; if (-1 != semctl(semid, 0, IPC_STAT, semarg)) { if (-1 == (semval = semctl(semid, semarg.buf->sem_nsems - 1, GETVAL)) || GTM_ID != semval) return FALSE; else { /* Make sure all has value = 0 */ for (semno = 0; semno < semarg.buf->sem_nsems - 1; semno++) if (-1 == (semval = semctl(semid, semno, GETVAL)) || semval) return FALSE; } return TRUE; } return FALSE; }