void mupip_rctldump(void) { # ifdef AUTORELINK_SUPPORTED unsigned short max_len; mstr dir; char objdir[GTM_PATH_MAX]; open_relinkctl_sgm *linkctl; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; if (TREF(parms_cnt)) { assert(1 == TREF(parms_cnt)); max_len = SIZEOF(objdir); if (!cli_get_str("DIRECTORY", objdir, &max_len)) rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_MUPCLIERR); dir.addr = objdir; dir.len = max_len; linkctl = relinkctl_attach(&dir); assert(linkctl == TREF(open_relinkctl_list)); assert((NULL == linkctl) || (NULL == linkctl->next)); } else zro_init(); util_out_print("", RESET); /* Reset output buffer */ zshow_rctldump(NULL); /* callee knows caller is mupip_rctldump type based on the NULL parameter */ # endif /* AUTORELINK_SUPPORTED */ }
void op_zrupdate(int argcnt, ...) { mval *objfilespec; va_list var; mval objpath; char tranbuf[MAX_FBUFF + 1], *chptr, chr; open_relinkctl_sgm *linkctl; relinkrec_t *rec; plength plen; int status, fextlen, fnamlen, fcnt; parse_blk pblk; struct stat outbuf; int stat_res; boolean_t seenfext, fileexists; mstr objdir, rtnname; uint4 hash, prev_hash_index; /* Currently only expecting one value per invocation right now. That will change in phase 2 hence the stdarg setup */ va_start(var, argcnt); assert(1 == argcnt); objfilespec = va_arg(var, mval *); va_end(var); MV_FORCE_STR(objfilespec); /* First some pre-processing to determine if an explicit file name or type was specified. If so, it must be ".o" for * this phase of implementation. Later phases may allow ".m" to be specified but not initially. Use parse_file() to * parse everything out and isolate any extention. */ memset(&pblk, 0, SIZEOF(pblk)); pblk.buffer = tranbuf; pblk.buff_size = (unsigned char)(MAX_FBUFF); /* Pass size of buffer - 1 (standard protocol for parse_file) */ pblk.def1_buf = DOTOBJEXT; /* Default .o file type if not specified */ pblk.def1_size = SIZEOF(DOTOBJEXT) - 1; pblk.fop = F_SYNTAXO; /* Syntax check only - bypass directory existence check */ status = parse_file(&objfilespec->str, &pblk); if (ERR_PARNORMAL != status) rts_error_csa(CSA_ARG(NULL) VARLSTCNT(5) ERR_FILEPARSE, 2, objfilespec->str.len, objfilespec->str.addr, status); tranbuf[pblk.b_esl] = '\0'; /* Needed for subsequent STAT_FILE */ seenfext = FALSE; if (0 != pblk.b_ext) { /* If a file extension was specified - get the extension sans any potential wildcard character */ for (chptr = pblk.l_ext + 1, fextlen = pblk.b_ext - 1; 0 < fextlen; chptr++, fextlen--) { /* Check each character in the extension except first which is the dot if ext exists at all */ if (WILDCARD != *chptr) { /* We see a char that isn't a wildcard character. If we've already seen our "o" file extension, * this char makes our requirement filetype impossible so raise an error. */ if (seenfext || (OBJEXT != *chptr)) /* No return from this error */ rts_error_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_FILEPARSE, 2, objfilespec->str.len, objfilespec->str.addr, ERR_TEXT, 2, RTS_ERROR_TEXT("Unsupported filetype specified")); seenfext = TRUE; } } } /* Do a simlar check for the file type */ if (0 != pblk.b_name) { /* A file name was specified (if not, tiz probably hard to find the file but that can be dealt with later). * Like in the above, the name must be comprised of valid chars for routine names. */ for (chptr = pblk.l_name, fnamlen = pblk.b_name; 0 < fnamlen; chptr++, fnamlen--) { if (WILDCARD != *chptr) { /* Substitute '%' for '_'. While this substitution is really only valid on the first char, only the * first char check allows "%" so a 2nd or later char check would fail the '%' substitution anyway. */ chr = ('_' == *chptr) ? '%' : *chptr; /* We see a char that isn't a wildcard character. If this is the first character, it can be * alpha or percent. If the second or later character, it can be alphanumeric. */ if (((fnamlen != pblk.b_name) && !VALID_MNAME_NFCHAR(chr)) /* 2nd char or later check */ || ((fnamlen == pblk.b_name) && !VALID_MNAME_FCHAR(chr))) /* 1st char check */ { rts_error_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_FILEPARSE, 2, objfilespec->str.len, objfilespec->str.addr, ERR_TEXT, 2, RTS_ERROR_TEXT("Filename is not a valid routine name")); } } } } /* When specifying a non-wildcarded object file, it is possible for the file to have been removed, in which case we still * need to update its relinkctl entry (if exists) to notify other processes about the object's deletion. */ if (!(pblk.fnb & F_WILD) & seenfext) { /* If no wildcards in path and saw the extension we want - no need to wash through zsearch() */ objdir.addr = pblk.l_dir; objdir.len = pblk.b_dir; linkctl = relinkctl_attach(&objdir); /* Create/attach/open relinkctl file */ if (NULL == linkctl) /* Non-existant path and no associated relinkctl file */ /* Note this reference to errno depends on nothing in relinkctl_attach() doing anything to modify * errno after the realpath() call. No return from this error. */ rts_error_csa(CSA_ARG(NULL) VARLSTCNT(5) ERR_FILEPARSE, 2, objfilespec->str.len, objfilespec->str.addr, errno); /* What we do at this point depends on the following conditions: * * 1) If the specified file exists, we can add it to relinkctl file and/or update its cycle. * 2) If the file doesn't exist on disk but the routine is found in the relinkctl file, update cycle. * 3) If no file and no entry, just ignore it and do nothing (info error removed by request). */ STAT_FILE(tranbuf, &outbuf, stat_res); if (-1 == stat_res) { if (ENOENT != errno) rts_error_csa(CSA_ARG(NULL) VARLSTCNT(5) ERR_FILEPARSE, 2, objfilespec->str.len, objfilespec->str.addr, errno); fileexists = FALSE; } else fileexists = TRUE; rtnname.len = pblk.b_name; rtnname.addr = pblk.l_name; CONVERT_FILENAME_TO_RTNNAME(rtnname); /* Set rtnname right before searching in relinkctl file */ assert(valid_mname(&rtnname)); COMPUTE_RELINKCTL_HASH(&rtnname, hash); rec = relinkctl_find_record(linkctl, &rtnname, hash, &prev_hash_index); if ((NULL == rec) && !fileexists) return; /* No file and no entry - ignore */ /* Either the file exists or the entry exists so add or update it */ rec = relinkctl_insert_record(linkctl, &rtnname); RELINKCTL_CYCLE_INCR(rec, linkctl); /* Increment cycle indicating change to world */ return; } /* If we have a wildcarded request or one without the object filetype, reprocess the string with $ZSEARCH using our * defined stream to resolve wildcards. Then loop through processing each file returned. In this loop, we just ignore * any file that doesn't have a ".o" extension. */ op_fnzsearch((mval *)&literal_null, STRM_ZRUPDATE, 0, &objpath); /* Clear any existing cache */ for (fcnt = 0; ; fcnt++) { plen.p.pint = op_fnzsearch(objfilespec, STRM_ZRUPDATE, 0, &objpath); if (0 == objpath.str.len) { /* End of file list */ if (0 == fcnt) /* Still looking to process our first file - give no objects found message as a "soft" message * (INFO level message - supressed in other than direct mode) */ rts_error_csa(CSA_ARG(NULL) VARLSTCNT(8) MAKE_MSG_INFO(ERR_FILEPARSE), 2, objfilespec->str.len, objfilespec->str.addr, ERR_TEXT, 2, RTS_ERROR_TEXT("No object files found")); break; } /* The extension contains the extension-start character ('.') so we are looking for the extension '.o' hence * the length must be 2 and the 2nd char must be OBJEXT. */ if ((2 == plen.p.pblk.b_ext) && (OBJEXT == *(objpath.str.addr + plen.p.pblk.b_dir + plen.p.pblk.b_name + 1))) { /* This is (probably) an object file. Double check file is a file and not a directory */ memcpy(tranbuf, objpath.str.addr, objpath.str.len); /* Need null terminated version for STAT */ tranbuf[objpath.str.len] = '\0'; /* Not guaranteed null termination from op_fnzsearch */ STAT_FILE(tranbuf, &outbuf, stat_res); /* If either something happened to the file since op_fnzsearch() saw it or the file is not a file, then * ignore it. */ if ((-1 == stat_res) || !S_ISREG(outbuf.st_mode)) { fcnt--; /* Don't count files not found for whatever reason */ continue; } /* Before opening the relinkctl file, verify we actually do have a valid routine name */ rtnname.len = plen.p.pblk.b_name; rtnname.addr = objpath.str.addr + plen.p.pblk.b_dir; CONVERT_FILENAME_TO_RTNNAME(rtnname); /* Set rtnname right before searching in relinkctl file */ if (!valid_mname(&rtnname)) { fcnt--; continue; /* Ignore files that are invalid wildcard matches */ } objdir.addr = objpath.str.addr; objdir.len = plen.p.pblk.b_dir; linkctl = relinkctl_attach(&objdir); /* Create/attach/open relinkctl file */ if (NULL == linkctl) { fcnt--; /* Path disappeared - don't count it */ continue; } rec = relinkctl_insert_record(linkctl, &rtnname); RELINKCTL_CYCLE_INCR(rec, linkctl); /* Increment cycle indicating change to world */ } else fcnt--; /* Don't count ignored files */ } }
/* Routine to parse the value of $ZROUTINES and create the list of structures that define the (new) routine * search list order and define which (if any) directories can use auto-relink. * * Parameter: * str - string to parse (usually dollar_zroutines) * * Return code: * none */ void zro_load(mstr *str) { unsigned toktyp, status; boolean_t enable_autorelink; mstr tok, transtr; char *lp, *top; zro_ent array[ZRO_MAX_ENTS], *op; int oi, si, total_ents; struct stat outbuf; int stat_res; char tranbuf[MAX_FBUFF + 1]; parse_blk pblk; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; ARLINK_ONLY(TREF(arlink_enabled) = FALSE); /* Set if any zro entry is enabled for autorelink */ memset(array, 0, SIZEOF(array)); lp = str->addr; top = lp + str->len; while ((lp < top) && (ZRO_DEL == *lp)) /* Bypass leading blanks */ lp++; array[0].type = ZRO_TYPE_COUNT; array[0].count = 0; memset(&pblk, 0, SIZEOF(pblk)); pblk.buffer = tranbuf; toktyp = GETTOK; if (ZRO_EOL == toktyp) { /* Null string - set default - implies current working directory only */ array[0].count = 1; array[1].type = ZRO_TYPE_OBJECT; array[1].str.len = 0; array[2].type = ZRO_TYPE_COUNT; array[2].count = 1; array[3].type = ZRO_TYPE_SOURCE; array[3].str.len = 0; si = 4; } else { /* String supplied - parse it */ for (oi = 1;;) { if (ZRO_IDN != toktyp) rts_error_csa(CSA_ARG(NULL) VARLSTCNT(5) ERR_ZROSYNTAX, 2, str->len, str->addr, ERR_FSEXP); if (ZRO_MAX_ENTS <= (oi + 1)) rts_error_csa(CSA_ARG(NULL) VARLSTCNT(7) ERR_ZROSYNTAX, 2, str->len, str->addr, ERR_MAXARGCNT, 1, ZRO_MAX_ENTS); /* We have type ZRO_IDN (an identifier/name of some sort). See if token has a "*" (ZRO_ALF) at the end * of it indicating that it is supposed to (1) be a directory and not a shared library and (2) that the * user desires this directory to have auto-relink capability. */ enable_autorelink = FALSE; /* All platforms allow the auto-relink indicator on object directories but only autorelink able platforms * (#ifdef AUTORELINK_SUPPORTED is set) do anything with it. Other platforms just ignore it. Specifying * "*" at end of non-object directories causes an error further downstream (FILEPARSE) when the "*" is * not stripped off the file name - unless someone has managed to create a directory with a "*" suffix. */ if (ZRO_ALF == *(tok.addr + tok.len - 1)) { /* Auto-relink is indicated */ enable_autorelink = TRUE; TREF(arlink_enabled) = TRUE; --tok.len; /* Remove indicator from name so we can use it */ assert(0 <= tok.len); } if (SIZEOF(tranbuf) <= tok.len) rts_error_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_ZROSYNTAX, 2, str->len, str->addr, ERR_FILEPARSE, 2, tok.len, tok.addr); /* Run specified directory through parse_file to fill in any missing pieces and get some info on it */ pblk.buff_size = MAX_FBUFF; /* Don't count null terminator here */ pblk.fnb = 0; status = parse_file(&tok, &pblk); if (!(status & 1)) rts_error_csa(CSA_ARG(NULL) VARLSTCNT(9) ERR_ZROSYNTAX, 2, str->len, str->addr, ERR_FILEPARSE, 2, tok.len, tok.addr, status); tranbuf[pblk.b_esl] = 0; /* Needed for some subsequent STAT_FILE */ STAT_FILE(tranbuf, &outbuf, stat_res); if (-1 == stat_res) rts_error_csa(CSA_ARG(NULL) VARLSTCNT(9) ERR_ZROSYNTAX, 2, str->len, str->addr, ERR_FILEPARSE, 2, tok.len, tok.addr, errno); if (S_ISREG(outbuf.st_mode)) { /* Regular file - a shared library file */ if (enable_autorelink) /* Auto-relink indicator on shared library not permitted */ rts_error_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_ZROSYNTAX, 2, str->len, str->addr, ERR_FILEPARSE, 2, tok.len, tok.addr); array[oi].shrlib = zro_shlibs_find(tranbuf); array[oi].type = ZRO_TYPE_OBJLIB; si = oi + 1; } else { if (!S_ISDIR(outbuf.st_mode)) rts_error_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_ZROSYNTAX, 2, str->len, str->addr, ERR_INVZROENT, 2, tok.len, tok.addr); array[oi].type = ZRO_TYPE_OBJECT; array[oi + 1].type = ZRO_TYPE_COUNT; si = oi + 2; # ifdef AUTORELINK_SUPPORTED # ifdef DEBUG /* If env var gtm_test_autorelink_always is set in dbg version, treat every * object directory specified in $zroutines as if * has been additionally specified. */ if (TREF(gtm_test_autorelink_always)) { enable_autorelink = TRUE; TREF(arlink_enabled) = TRUE; } # endif if (enable_autorelink) { /* Only setup autorelink struct if it is enabled */ if (!TREF(is_mu_rndwn_rlnkctl)) { transtr.addr = tranbuf; transtr.len = pblk.b_esl; array[oi].relinkctl_sgmaddr = (void_ptr_t)relinkctl_attach(&transtr, NULL, 0); } else { /* If zro_load() is called as a part of MUPIP RUNDOWN -RELINKCTL, then we do not * want to do relinkctl_attach() on all relinkctl files at once because we leave * the function holding the linkctl lock, which might potentially cause a deadlock * if multiple processes are run concurrently with different $gtmroutines. However, * we need a way to tell mu_rndwn_rlnkctl() which object directories are autorelink- * enabled. For that we set a negative number to the presently unused count field of * object directory entries in the zro_ent linked list. If we ever decide to make * that value meaningful, then, perhaps, ensuring that this count remains negative * in case of MUPIP RUNDOWN -RELINKCTL but has the correct absolute value would do * the trick. */ array[oi].count = ZRO_DIR_ENABLE_AR; } } # endif } array[0].count++; array[oi].str = tok; toktyp = GETTOK; if (ZRO_LBR == toktyp) { if (ZRO_TYPE_OBJLIB == array[oi].type) rts_error_csa(CSA_ARG(NULL) VARLSTCNT(5) ERR_ZROSYNTAX, 2, str->len, str->addr, ERR_NOLBRSRC); toktyp = GETTOK; if (ZRO_DEL == toktyp) toktyp = GETTOK; if ((ZRO_IDN != toktyp) && (ZRO_RBR != toktyp)) rts_error_csa(CSA_ARG(NULL) VARLSTCNT(5) ERR_ZROSYNTAX, 2, str->len, str->addr, ERR_QUALEXP); array[oi + 1].count = 0; for (;;) { if (ZRO_RBR == toktyp) break; if (ZRO_IDN != toktyp) rts_error_csa(CSA_ARG(NULL) VARLSTCNT(5) ERR_ZROSYNTAX, 2, str->len, str->addr, ERR_FSEXP); if (ZRO_MAX_ENTS <= si) rts_error_csa(CSA_ARG(NULL) VARLSTCNT(7) ERR_ZROSYNTAX, 2, str->len, str->addr, ERR_MAXARGCNT, 1, ZRO_MAX_ENTS); if (SIZEOF(tranbuf) <= tok.len) rts_error_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_ZROSYNTAX, 2, str->len, str->addr, ERR_FILEPARSE, 2, tok.len, tok.addr); pblk.buff_size = MAX_FBUFF; pblk.fnb = 0; status = parse_file(&tok, &pblk); if (!(status & 1)) rts_error_csa(CSA_ARG(NULL) VARLSTCNT(9) ERR_ZROSYNTAX, 2, str->len, str->addr, ERR_FILEPARSE, 2, tok.len, tok.addr, status); tranbuf[pblk.b_esl] = 0; STAT_FILE(tranbuf, &outbuf, stat_res); if (-1 == stat_res) rts_error_csa(CSA_ARG(NULL) VARLSTCNT(9) ERR_ZROSYNTAX, 2, str->len, str->addr, ERR_FILEPARSE, 2, tok.len, tok.addr, errno); if (!S_ISDIR(outbuf.st_mode)) rts_error_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_ZROSYNTAX, 2, str->len, str->addr, ERR_DIRONLY, 2, tok.len, tok.addr); array[oi + 1].count++; array[si].type = ZRO_TYPE_SOURCE; array[si].str = tok; si++; toktyp = GETTOK; if (ZRO_DEL == toktyp) toktyp = GETTOK; } toktyp = GETTOK; } else { if ((ZRO_TYPE_OBJLIB != array[oi].type) && ((ZRO_DEL == toktyp) || (ZRO_EOL == toktyp))) { if (ZRO_MAX_ENTS <= si) rts_error_csa(CSA_ARG(NULL) VARLSTCNT(7) ERR_ZROSYNTAX, 2, str->len, str->addr, ERR_MAXARGCNT, 1, ZRO_MAX_ENTS); array[oi + 1].count = 1; array[si] = array[oi]; array[si].type = ZRO_TYPE_SOURCE; si++; } } if (ZRO_EOL == toktyp) break; if (ZRO_DEL == toktyp) toktyp = GETTOK; else rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_ZROSYNTAX, 2, str->len, str->addr); oi = si; } } total_ents = si; if (TREF(zro_root)) { assert((TREF(zro_root))->type == ZRO_TYPE_COUNT); oi = (TREF(zro_root))->count; assert(oi); for (op = TREF(zro_root) + 1; 0 < oi--;) { /* Release space held by translated entries */ assert((ZRO_TYPE_OBJECT == op->type) || (ZRO_TYPE_OBJLIB == op->type)); if (op->str.len) free(op->str.addr); if (ZRO_TYPE_OBJLIB == (op++)->type) continue; /* i.e. no sources for shared library */ assert(ZRO_TYPE_COUNT == op->type); si = (op++)->count; for (; si-- > 0; op++) { assert(ZRO_TYPE_SOURCE == op->type); if (op->str.len) free(op->str.addr); } } free(TREF(zro_root)); } TREF(zro_root) = (zro_ent *)malloc(total_ents * SIZEOF(zro_ent)); memcpy((uchar_ptr_t)TREF(zro_root), (uchar_ptr_t)array, total_ents * SIZEOF(zro_ent)); assert(ZRO_TYPE_COUNT == (TREF(zro_root))->type); oi = (TREF(zro_root))->count; assert(oi); for (op = TREF(zro_root) + 1; 0 < oi--;) { assert((ZRO_TYPE_OBJECT == op->type) || (ZRO_TYPE_OBJLIB == op->type)); if (op->str.len) { pblk.buff_size = MAX_FBUFF; pblk.fnb = 0; status = parse_file(&op->str, &pblk); if (!(status & 1)) rts_error_csa(CSA_ARG(NULL) VARLSTCNT(9) ERR_ZROSYNTAX, 2, str->len, str->addr, ERR_FILEPARSE, 2, op->str.len, op->str.addr, status); op->str.addr = (char *)malloc(pblk.b_esl); op->str.len = pblk.b_esl; memcpy(op->str.addr, pblk.buffer, pblk.b_esl); } if (ZRO_TYPE_OBJLIB == (op++)->type) continue; assert(ZRO_TYPE_COUNT == op->type); si = (op++)->count; for (; 0 < si--; op++) { assert(ZRO_TYPE_SOURCE == op->type); if (op->str.len) { pblk.buff_size = MAX_FBUFF; pblk.fnb = 0; status = parse_file(&op->str, &pblk); if (!(status & 1)) rts_error_csa(CSA_ARG(NULL) VARLSTCNT(9) ERR_ZROSYNTAX, 2, str->len, str->addr, ERR_FILEPARSE, 2, op->str.len, op->str.addr, status); op->str.addr = (char *)malloc(pblk.b_esl); op->str.len = pblk.b_esl; memcpy(op->str.addr, pblk.buffer, pblk.b_esl); } } } (TREF(set_zroutines_cycle))++; /* Signal need to recompute zroutines histories for each linked routine */ }