void AstNode::dump(ostream& str) { str<<typeName()<<" "<<(void*)this //<<" "<<(void*)this->m_backp <<" <e"<<dec<<editCount() <<((editCount()>=editCountLast())?"#>":">") <<" {"<<fileline()->filenameLetters()<<dec<<fileline()->lineno()<<"}"; if (user1p()) str<<" u1="<<(void*)user1p(); if (user2p()) str<<" u2="<<(void*)user2p(); if (user3p()) str<<" u3="<<(void*)user3p(); if (user4p()) str<<" u4="<<(void*)user4p(); if (user5p()) str<<" u5="<<(void*)user5p(); if (hasDType()) { if (dtypep()==this) str<<" @dt="<<"this@"; else str<<" @dt="<<(void*)dtypep()<<"@"; // Final @ so less likely to by accident think it's nodep if (AstNodeDType* dtp = dtypep()) { dtp->dumpSmall(str); } } else { // V3Broken will throw an error if (dtypep()) str<<" %Error-dtype-exp=null,got="<<(void*)dtypep(); } if (name()!="") { if (castConst()) str<<" "<<name(); // Already quoted else str<<" "<<V3Number::quoteNameControls(name()); } }
void AstNode::dump(ostream& str) { str<<typeName()<<" "<<(void*)this //<<" "<<(void*)this->m_backp <<" <e"<<dec<<editCount() <<((editCount()>=editCountLast())?"#>":">") <<" {"<<fileline()->filenameLetters()<<dec<<fileline()->lineno()<<"}" <<" "<<(isSigned()?"s":"") <<(isDouble()?"d":"") <<"w"<<(widthSized()?"":"u")<<width(); if (!widthSized()) str<<"/"<<widthMin(); if (name()!="") str<<" "<<AstNode::quoteName(name()); }
void xptrace(Map *map, uvlong pc, uvlong sp, Symbol *sym) { char buf[1024]; if(sym == nil){ fprint(2, "syms\n"); return; } if(histograms) addtohistogram(nextpc, pc, sp); if(!histograms || stacks > 1 || pprof) { if(nextpc == 0) nextpc = sym->value; if(stacks){ fprint(2, "%s(", sym->name); fprint(2, ")"); if(nextpc != sym->value) fprint(2, "+%#llux ", nextpc - sym->value); if(have_syms && linenums && fileline(buf, sizeof buf, pc)) { fprint(2, " %s", buf); } fprint(2, "\n"); } if (pprof) { addppword(nextpc); } } nextpc = pc; }
string V3PreProcImp::defParams(const string& name) { DefinesMap::iterator iter = m_defines.find(name); if (iter == m_defines.end()) { fileline()->v3error("Define or directive not defined: `"+name); return ""; } return iter->second.params(); }
void V3ParseImp::lexFile(const string& modname) { // Prepare for lexing UINFO(3,"Lexing "<<modname<<endl); s_parsep = this; fileline()->warnResetDefault(); // Reenable warnings on each file lexDestroy(); // Restart from clean slate. lexNew(debugFlex()>=9); // Lex it if (bisonParse()) v3fatal("Cannot continue\n"); }
void printpc(Map *map, uvlong pc, uvlong sp) { char buf[1024]; if(registers) arch->regprint(); if(have_syms > 0 && linenums && fileline(buf, sizeof buf, pc)) fprint(2, "%s\n", buf); if(have_syms > 0 && functions) { symoff(buf, sizeof(buf), pc, CANY); fprint(2, "%s\n", buf); } if(stacks || pprof){ stacktracepcsp(map, pc, sp); } else if(histograms){ addtohistogram(pc, 0, sp); } }
int V3PreProcImp::getStateToken() { // Return the next state-determined token while (1) { next_tok: if (isEof()) return VP_EOF; int tok = getRawToken(); ProcState state = m_states.top(); // Most states emit white space and comments between tokens. (Unless collecting a string) if (tok==VP_WHITE && state !=ps_STRIFY) return (tok); if (tok==VP_BACKQUOTE && state !=ps_STRIFY) { tok = VP_TEXT; } if (tok==VP_COMMENT) { if (!m_off) { if (m_lexp->m_keepComments == KEEPCMT_SUB) { string rtn; rtn.assign(yyourtext(),yyourleng()); comment(rtn); // Need to insure "foo/**/bar" becomes two tokens insertUnreadback (" "); } else if (m_lexp->m_keepComments) { return (tok); } else { // Need to insure "foo/**/bar" becomes two tokens insertUnreadback (" "); } } // We're off or processed the comment specially. If there are newlines // in it, we also return the newlines as TEXT so that the linenumber // count is maintained for downstream tools for (size_t len=0; len<(size_t)yyourleng(); len++) { if (yyourtext()[len]=='\n') m_lineAdd++; } goto next_tok; } if (tok==VP_LINE) { addLineComment(m_lexp->m_enterExit); goto next_tok; } if (tok==VP_DEFREF_JOIN) { // Here's something fun and unspecified as yet: // The existance of non-existance of a base define changes `` expansion // `define QA_b zzz // `define Q1 `QA``_b // 1Q1 -> zzz // `define QA a // `Q1 -> a_b // Note parenthesis make this unambiguous // `define Q1 `QA()``_b // -> a_b // This may be a side effect of how `UNDEFINED remains as `UNDEFINED, // but it screws up our method here. So hardcode it. string name (yyourtext()+1,yyourleng()-1); if (defExists(name)) { // JOIN(DEFREF) // Put back the `` and process the defref UINFO(5,"```: define "<<name<<" exists, expand first\n"); m_defPutJoin = true; // After define, unputString("``"). Not now as would lose yyourtext() UINFO(5,"TOKEN now DEFREF\n"); tok = VP_DEFREF; } else { // DEFREF(JOIN) UINFO(5,"```: define "<<name<<" doesn't exist, join first\n"); // FALLTHRU, handle as with VP_SYMBOL_JOIN } } if (tok==VP_SYMBOL_JOIN || tok==VP_DEFREF_JOIN) { // not else if, can fallthru from above if() // a`` -> string doesn't include the ``, so can just grab next and continue string out (yyourtext(),yyourleng()); UINFO(5,"`` LHS:"<<out<<endl); // a``b``c can have multiple joins, so we need a stack m_joinStack.push(out); statePush(ps_JOIN); goto next_tok; } // Deal with some special parser states switch (state) { case ps_TOP: { break; } case ps_DEFNAME_UNDEF: // FALLTHRU case ps_DEFNAME_DEFINE: // FALLTHRU case ps_DEFNAME_IFDEF: // FALLTHRU case ps_DEFNAME_IFNDEF: // FALLTHRU case ps_DEFNAME_ELSIF: { if (tok==VP_SYMBOL) { m_lastSym.assign(yyourtext(),yyourleng()); if (state==ps_DEFNAME_IFDEF || state==ps_DEFNAME_IFNDEF) { bool enable = defExists(m_lastSym); UINFO(4,"Ifdef "<<m_lastSym<<(enable?" ON":" OFF")<<endl); if (state==ps_DEFNAME_IFNDEF) enable = !enable; m_ifdefStack.push(VPreIfEntry(enable,false)); if (!enable) parsingOff(); statePop(); goto next_tok; } else if (state==ps_DEFNAME_ELSIF) { if (m_ifdefStack.empty()) { error("`elsif with no matching `if\n"); } else { // Handle `else portion VPreIfEntry lastIf = m_ifdefStack.top(); m_ifdefStack.pop(); if (!lastIf.on()) parsingOn(); // Handle `if portion bool enable = !lastIf.everOn() && defExists(m_lastSym); UINFO(4,"Elsif "<<m_lastSym<<(enable?" ON":" OFF")<<endl); m_ifdefStack.push(VPreIfEntry(enable, lastIf.everOn())); if (!enable) parsingOff(); } statePop(); goto next_tok; } else if (state==ps_DEFNAME_UNDEF) { if (!m_off) { UINFO(4,"Undef "<<m_lastSym<<endl); undef(m_lastSym); } statePop(); goto next_tok; } else if (state==ps_DEFNAME_DEFINE) { // m_lastSym already set. stateChange(ps_DEFFORM); m_lexp->pushStateDefForm(); goto next_tok; } else fatalSrc("Bad case\n"); goto next_tok; } else if (tok==VP_TEXT) { // IE, something like comment between define and symbol if (!m_off) return tok; else goto next_tok; } else if (tok==VP_DEFREF) { // IE, `ifdef `MACRO(x): Substitue and come back here when state pops. break; } else { error((string)"Expecting define name. Found: "+tokenName(tok)+"\n"); goto next_tok; } } case ps_DEFFORM: { if (tok==VP_DEFFORM) { m_formals = m_lexp->m_defValue; if (debug()>=5) cout<<"DefFormals='"<<V3PreLex::cleanDbgStrg(m_formals)<<"'\n"; stateChange(ps_DEFVALUE); m_lexp->pushStateDefValue(); goto next_tok; } else if (tok==VP_TEXT) { // IE, something like comment in formals if (!m_off) return tok; else goto next_tok; } else { error((string)"Expecting define formal arguments. Found: "+tokenName(tok)+"\n"); goto next_tok; } } case ps_DEFVALUE: { static string newlines; newlines = "\n"; // Always start with trailing return if (tok == VP_DEFVALUE) { if (debug()>=5) cout<<"DefValue='"<<V3PreLex::cleanDbgStrg(m_lexp->m_defValue) <<"' formals='"<<V3PreLex::cleanDbgStrg(m_formals)<<"'\n"; // Add any formals string formals = m_formals; string value = m_lexp->m_defValue; // Remove returns // Not removing returns in values has two problems, // 1. we need to correct line numbers with `line after each substitution // 2. Substituting in " .... " with embedded returns requires \ escape. // This is very difficult in the presence of `", so we keep the \ before the newline. for (size_t i=0; i<formals.length(); i++) { if (formals[i] == '\n') { newlines += "\n"; } } for (size_t i=0; i<value.length(); i++) { if (value[i] == '\n') { newlines += "\n"; } } if (!m_off) { // Remove leading and trailing whitespace value = trimWhitespace(value, true); // Define it UINFO(4,"Define "<<m_lastSym<<" "<<formals <<" = '"<<V3PreLex::cleanDbgStrg(value)<<"'"<<endl); define(fileline(), m_lastSym, value, formals, false); } } else { string msg = string("Bad define text, unexpected ")+tokenName(tok)+"\n"; fatalSrc(msg); } statePop(); // DEFVALUE is terminated by a return, but lex can't return both tokens. // Thus, we emit a return here. yyourtext(newlines.c_str(), newlines.length()); return(VP_WHITE); } case ps_DEFPAREN: { if (tok==VP_TEXT && yyourleng()==1 && yyourtext()[0]=='(') { stateChange(ps_DEFARG); goto next_tok; } else { if (m_defRefs.empty()) fatalSrc("Shouldn't be in DEFPAREN w/o active defref"); V3DefineRef* refp = &(m_defRefs.top()); error((string)"Expecting ( to begin argument list for define reference `"+refp->name()+"\n"); statePop(); goto next_tok; } } case ps_DEFARG: { if (m_defRefs.empty()) fatalSrc("Shouldn't be in DEFARG w/o active defref"); V3DefineRef* refp = &(m_defRefs.top()); refp->nextarg(refp->nextarg()+m_lexp->m_defValue); m_lexp->m_defValue=""; UINFO(4,"defarg++ "<<refp->nextarg()<<endl); if (tok==VP_DEFARG && yyourleng()==1 && yyourtext()[0]==',') { refp->args().push_back(refp->nextarg()); stateChange(ps_DEFARG); m_lexp->pushStateDefArg(1); refp->nextarg(""); goto next_tok; } else if (tok==VP_DEFARG && yyourleng()==1 && yyourtext()[0]==')') { // Substitute in and prepare for next action // Similar code in non-parenthesized define (Search for END_OF_DEFARG) refp->args().push_back(refp->nextarg()); string out; if (!m_off) { out = defineSubst(refp); //NOP: out = m_preprocp->defSubstitute(out); } m_defRefs.pop(); refp=NULL; if (m_defRefs.empty()) { statePop(); if (!m_off) unputDefrefString(out); m_lexp->m_parenLevel = 0; } else { // Finished a defref inside a upper defref // Can't subst now, or // `define a(ign) x,y // foo(`a(ign),`b) would break because a contains comma refp = &(m_defRefs.top()); // We popped, so new top refp->nextarg(refp->nextarg()+m_lexp->m_defValue+out); m_lexp->m_defValue=""; m_lexp->m_parenLevel = refp->parenLevel(); statePop(); // Will go to ps_DEFARG, as we're under another define } goto next_tok; } else if (tok==VP_DEFREF) { // Expand it, then state will come back here // Value of building argument is data before the lower defref // we'll append it when we push the argument. break; } else if (tok==VP_SYMBOL || tok==VP_STRING || VP_TEXT || VP_WHITE) { string rtn; rtn.assign(yyourtext(),yyourleng()); refp->nextarg(refp->nextarg()+rtn); goto next_tok; } else { error((string)"Expecting ) or , to end argument list for define reference. Found: "+tokenName(tok)); statePop(); goto next_tok; } } case ps_INCNAME: { if (tok==VP_STRING) { statePop(); m_lastSym.assign(yyourtext(),yyourleng()); UINFO(4,"Include "<<m_lastSym<<endl); // Drop leading and trailing quotes. m_lastSym.erase(0,1); m_lastSym.erase(m_lastSym.length()-1,1); include(m_lastSym); goto next_tok; } else if (tok==VP_TEXT && yyourleng()==1 && yyourtext()[0]=='<') { // include <filename> stateChange(ps_INCNAME); // Still m_lexp->pushStateIncFilename(); goto next_tok; } else if (tok==VP_DEFREF || tok==VP_STRIFY) { // Expand it, then state will come back here break; } else { statePop(); error((string)"Expecting include filename. Found: "+tokenName(tok)+"\n"); goto next_tok; } } case ps_ERRORNAME: { if (tok==VP_STRING) { if (!m_off) { m_lastSym.assign(yyourtext(),yyourleng()); error(m_lastSym); } statePop(); goto next_tok; } else { error((string)"Expecting `error string. Found: "+tokenName(tok)+"\n"); statePop(); goto next_tok; } } case ps_JOIN: { if (tok==VP_SYMBOL || tok==VP_TEXT) { if (m_joinStack.empty()) fatalSrc("`` join stack empty, but in a ``"); string lhs = m_joinStack.top(); m_joinStack.pop(); UINFO(5,"`` LHS:"<<lhs<<endl); string rhs (yyourtext(),yyourleng()); UINFO(5,"`` RHS:"<<rhs<<endl); string out = lhs+rhs; UINFO(5,"`` Out:"<<out<<endl); unputString(out); statePop(); goto next_tok; } else if (tok==VP_EOF || tok==VP_WHITE || tok == VP_COMMENT || tok==VP_STRING) { error((string)"Expecting symbol to terminate ``; whitespace etc cannot follow ``. Found: "+tokenName(tok)+"\n"); statePop(); goto next_tok; } else { // `define, etc, fall through and expand. Pop back here. break; } } case ps_STRIFY: { if (tok==VP_STRIFY) { // Quote what's in the middle of the stringification // Note a `" MACRO_WITH(`") `" doesn't need to be handled (we don't need a stack) // That behavior isn't specified, and other simulators vary widely string out = m_strify; m_strify = ""; // Convert any newlines to spaces, so we don't get a multiline "..." without \ escapes // The spec is silent about this either way; simulators vary string::size_type pos; while ((pos=out.find("\n")) != string::npos) { out.replace(pos, 1, " "); } unputString((string)"\""+out+"\""); statePop(); goto next_tok; } else if (tok==VP_EOF) { error("`\" not terminated at EOF\n"); } else if (tok==VP_BACKQUOTE) { m_strify += "\\\""; goto next_tok; } else if (tok==VP_DEFREF) { // Spec says to expand macros inside `" // Substitue it into the stream, then return here break; } else { // Append token to eventual string m_strify.append(yyourtext(),yyourleng()); goto next_tok; } } default: fatalSrc("Bad case\n"); } // Default is to do top level expansion of some tokens switch (tok) { case VP_INCLUDE: if (!m_off) { statePush(ps_INCNAME); } goto next_tok; case VP_UNDEF: statePush(ps_DEFNAME_UNDEF); goto next_tok; case VP_DEFINE: // No m_off check here, as a `ifdef NEVER `define FOO(`endif) should work statePush(ps_DEFNAME_DEFINE); goto next_tok; case VP_IFDEF: statePush(ps_DEFNAME_IFDEF); goto next_tok; case VP_IFNDEF: statePush(ps_DEFNAME_IFNDEF); goto next_tok; case VP_ELSIF: statePush(ps_DEFNAME_ELSIF); goto next_tok; case VP_ELSE: if (m_ifdefStack.empty()) { error("`else with no matching `if\n"); } else { VPreIfEntry lastIf = m_ifdefStack.top(); m_ifdefStack.pop(); bool enable = !lastIf.everOn(); UINFO(4,"Else "<<(enable?" ON":" OFF")<<endl); m_ifdefStack.push(VPreIfEntry(enable, lastIf.everOn())); if (!lastIf.on()) parsingOn(); if (!enable) parsingOff(); } goto next_tok; case VP_ENDIF: UINFO(4,"Endif "<<endl); if (m_ifdefStack.empty()) { error("`endif with no matching `if\n"); } else { VPreIfEntry lastIf = m_ifdefStack.top(); m_ifdefStack.pop(); if (!lastIf.on()) parsingOn(); // parsingOn() really only enables parsing if // all ifdef's above this want it on } goto next_tok; case VP_DEFREF: { // m_off not right here, but inside substitution, to make this work: `ifdef NEVER `DEFUN(`endif) string name (yyourtext()+1,yyourleng()-1); UINFO(4,"DefRef "<<name<<endl); if (m_defPutJoin) { m_defPutJoin = false; unputString("``"); } if (m_defDepth++ > V3PreProc::DEFINE_RECURSION_LEVEL_MAX) { error("Recursive `define substitution: `"+name); goto next_tok; } // substitute if (!defExists(name)) { // Not found, return original string as-is m_defDepth = 0; UINFO(4,"Defref `"<<name<<" => not_defined"<<endl); if (m_off) { goto next_tok; } else { return (VP_TEXT); } } else { string params = defParams(name); if (params=="0" || params=="") { // Found, as simple substitution if (m_off) { goto next_tok; } else { V3DefineRef tempref(name, ""); string out = defineSubst(&tempref); // Similar code in parenthesized define (Search for END_OF_DEFARG) //NOP: out = m_preprocp->defSubstitute(out); if (m_defRefs.empty()) { // Just output the substitution unputDefrefString(out); } else { // Inside another define. // Can't subst now, or // `define a x,y // foo(`a,`b) would break because a contains comma V3DefineRef* refp = &(m_defRefs.top()); refp->nextarg(refp->nextarg()+m_lexp->m_defValue+out); m_lexp->m_defValue=""; } goto next_tok; } } else { // Found, with parameters UINFO(4,"Defref `"<<name<<" => parameterized"<<endl); // The CURRENT macro needs the paren saved, it's not a property of the child macro if (!m_defRefs.empty()) m_defRefs.top().parenLevel(m_lexp->m_parenLevel); m_defRefs.push(V3DefineRef(name, params)); statePush(ps_DEFPAREN); m_lexp->pushStateDefArg(0); goto next_tok; } } fatalSrc("Bad case\n"); } case VP_ERROR: { statePush(ps_ERRORNAME); goto next_tok; } case VP_EOF: if (!m_ifdefStack.empty()) { error("`ifdef not terminated at EOF\n"); } return tok; case VP_UNDEFINEALL: if (!m_off) { UINFO(4,"Undefineall "<<endl); undefineall(); } goto next_tok; case VP_STRIFY: // We must expand macros in the body of the stringification // Then, when done, form a final string to return // (it could be used as a include filename, for example, so need the string token) statePush(ps_STRIFY); goto next_tok; case VP_SYMBOL: case VP_STRING: case VP_TEXT: { m_defDepth = 0; if (!m_off) return tok; else goto next_tok; } case VP_WHITE: // Handled at top of loop case VP_COMMENT: // Handled at top of loop case VP_DEFFORM: // Handled by state=ps_DEFFORM; case VP_DEFVALUE: // Handled by state=ps_DEFVALUE; default: fatalSrc((string)"Internal error: Unexpected token "+tokenName(tok)+"\n"); break; } return tok; } }
void V3PreProcImp::comment(const string& text) { // Comment detected. Only keep relevant data. // if (text =~ m!^\/[\*\/]\s*[vV]erilator\s*(.*$)!) { // string cmd = $1; // cmd =~ s!\s*(\*\/)\s*$!!; // cmd =~ s!\s+! !g; // cmd =~ s!\s+$!!g; const char* cp = text.c_str(); if (cp[0]=='/' && (cp[1]=='/' || cp[1]=='*')) { cp+=2; } else return; while (isspace(*cp)) cp++; bool synth = false; if ((cp[0]=='v' || cp[0]=='V') && 0==(strncmp(cp+1,"erilator",8))) { cp+=strlen("verilator"); if (*cp == '_') fileline()->v3error("Extra underscore in meta-comment; use /*verilator {...}*/ not /*verilator_{...}*/"); } else if (0==(strncmp(cp,"synopsys",strlen("synopsys")))) { cp+=strlen("synopsys"); synth = true; if (*cp == '_') fileline()->v3error("Extra underscore in meta-comment; use /*synopsys {...}*/ not /*synopsys_{...}*/"); } else if (0==(strncmp(cp,"cadence",strlen("cadence")))) { cp+=strlen("cadence"); synth = true; } else if (0==(strncmp(cp,"pragma",strlen("pragma")))) { cp+=strlen("pragma"); synth = true; } else if (0==(strncmp(cp,"ambit synthesis",strlen("ambit synthesis")))) { cp+=strlen("ambit synthesis"); synth = true; } else { return; } if (*cp && !isspace(*cp)) return; while (isspace(*cp)) cp++; const char* ep = cp+strlen(cp); if (ep>cp && (ep[-1]=='/' || cp[-2]=='*')) ep-=2; while (ep>cp && (isspace(ep[-1]))) ep--; string cmd (cp, ep-cp); string::size_type pos; while ((pos=cmd.find("\"")) != string::npos) cmd.replace(pos, 1, " "); while ((pos=cmd.find("\t")) != string::npos) cmd.replace(pos, 1, " "); while ((pos=cmd.find(" ")) != string::npos) cmd.replace(pos, 2, " "); if (synth) { if (v3Global.opt.assertOn()) { // one_hot, one_cold, (full_case, parallel_case) if (commentTokenMatch(cmd/*ref*/, "full_case")) { insertUnreadback ("/*verilator full_case*/"); } if (commentTokenMatch(cmd/*ref*/, "parallel_case")) { insertUnreadback ("/*verilator parallel_case*/"); } //if (commentTokenMatch(cmd/*ref*/, "one_hot")) { // insertUnreadback ("/*verilator one_hot*/ "+cmd+";"); //} //if (commentTokenMatch(cmd/*ref*/, "one_cold")) { // insertUnreadback ("/*verilator one_cold*/ "+cmd+";"); //} // else ignore the comment we don't recognize } // else no assertions } else if ((pos=cmd.find("public_flat_rw")) != string::npos) { // "/*verilator public_flat_rw @(foo) */" -> "/*verilator public_flat_rw*/ @(foo)" cmd = cmd.substr(pos+strlen("public_flat_rw")); while (isspace(cmd[0])) cmd = cmd.substr(1); if ((pos=cmd.find("*/")) != string::npos) cmd.replace(pos, 2, ""); insertUnreadback ("/*verilator public_flat_rw*/ "+cmd+" /**/"); } else { insertUnreadback ("/*verilator "+cmd+"*/"); } }
void V3PreProcImp::include(const string& filename) { // Include seen. Ask the preprocessor shell to call back around to us V3PreShell::preprocInclude (fileline(), filename); }
void v3errorEnd(ostringstream& str) { fileline()->v3errorEnd(str); }
int main( int argc, char **argv) { scmcon *testconp = NULL; scmcon *realconp = NULL; scm *scmp = NULL; FILE *sfile = NULL; char *thedelfile = NULL; char *topdir = NULL; char *thefile = NULL; char *outfile = NULL; char *outfull = NULL; char *outdir = NULL; char *tmpdsn = NULL; char *ne; char *porto = NULL; char errmsg[1024]; char *skifile = NULL; int ians = 0; int do_create = 0; int do_delete = 0; int do_sockopts = 0; int do_fileopts = 0; int use_filelist = 0; int perpetual = 0; int really = 0; int trusted = 0; int force = 0; int allowex = 0; int sta = 0; int s; int c; (void)setbuf(stdout, NULL); if (argc <= 1) { usage(); return (1); } while ((c = getopt(argc, argv, "t:xyhad:f:F:lLwz:pm:c:s")) != EOF) { switch (c) { case 'a': allowex = 1; break; case 't': do_create++; topdir = optarg; break; case 'x': do_delete++; break; case 'y': force++; break; case 'D': trusted++; case 'd': thedelfile = optarg; break; case 'F': trusted++; case 'f': thefile = optarg; break; case 'L': trusted++; case 'l': use_filelist++; break; case 'w': do_sockopts++; break; case 'z': do_fileopts++; porto = optarg; break; case 'p': perpetual++; break; case 'c': skifile = optarg; break; case 'h': usage(); return (0); case 's': strict_profile_checks = 1; // global from myssl.c strict_profile_checks_cms = 1; // global from roa_validate.c break; default: (void)fprintf(stderr, "Invalid option '%c'\n", c); usage(); return (1); } } // if there is anything left in argv, or no operation specified, warn user if (optind < argc) { (void)printf("Extra arguments at the end of the command line.\n"); usage(); return (1); } if ((do_create + do_delete + do_sockopts + do_fileopts) == 0 && thefile == 0 && thedelfile == 0 && skifile == 0 && use_filelist == 0) { (void)printf("You need to specify at least one operation " "(e.g. -f file).\n"); usage(); return (1); } OPEN_LOG("rcli", LOG_USER); if (!my_config_load()) { LOG(LOG_ERR, "can't load configuration"); exit(EXIT_FAILURE); } if (force == 0) { if (do_delete > 0) { ians = yorn("Do you REALLY want to delete all database tables"); if (ians <= 0) { LOG(LOG_NOTICE, "Delete operation cancelled"); return (1); } really++; } if ((do_create > 0) && (really == 0)) { ians = yorn("Do you REALLY want to create all database tables"); if (ians <= 0) { LOG(LOG_NOTICE, "Create operation cancelled"); return (1); } really++; } } scmp = initscm(); if (scmp == NULL) { LOG(LOG_ERR, "Internal error: cannot initialize database schema"); return (-2); } /* * If a create or delete operation is being performed, then a test dsn * will be needed; create it now and defer the creation of the real dsn * until later. Otherwise, create the real dsn. * * A test dsn is needed for operations that operate on the overall * database state as opposed to the rpki tables, namely the create and * delete operations. */ if ((do_create + do_delete) > 0) { /* * Note that in the following line, we do not intend to edit * the database named "information_schema". We are simply * filling in the "database name" parameter with something * that is guaranteed to be valid for MySQL. */ tmpdsn = makedsnscm(scmp->dsnpref, "information_schema", CONFIG_DATABASE_USER_get(), CONFIG_DATABASE_PASSWORD_get()); if (tmpdsn == NULL) { membail(); return (-1); } testconp = connectscm(tmpdsn, errmsg, 1024); memset(tmpdsn, 0, strlen(tmpdsn)); free((void *)tmpdsn); if (testconp == NULL) { LOG(LOG_ERR, "Cannot connect to DSN: %s", errmsg); freescm(scmp); return (-1); } } else { realconp = connectscm(scmp->dsn, errmsg, 1024); if (realconp == NULL) { LOG(LOG_ERR, "Cannot connect to DSN %s: %s", scmp->dsn, errmsg); freescm(scmp); return (-1); } } /* * Process command line options in the following order: delete, create, * dofile, dodir, listener. */ if (do_delete > 0) sta = deleteop(testconp, scmp); if ((do_create > 0) && (sta == 0)) /* first phase of create */ sta = createop(testconp, scmp); /* * Don't need the test connection any more */ if (testconp != NULL) { disconnectscm(testconp); testconp = NULL; } /* * If there has been an error or if we're done because the database was * just deleted and not re-created, bail out. */ if (sta < 0 || (do_delete > 0 && do_create == 0)) { if (realconp != NULL) disconnectscm(realconp); freescm(scmp); if (tdir != NULL) free((void *)tdir); return (sta); } /* * If a connection to the real DSN has not been opened yet, open it now. */ if (realconp == NULL) { realconp = connectscm(scmp->dsn, errmsg, 1024); if (realconp == NULL) { LOG(LOG_ERR, "Cannot connect to DSN %s: %s", scmp->dsn, errmsg); freescm(scmp); if (tdir != NULL) free((void *)tdir); return (-1); } } /* * If a create operation was requested, complete it now. */ if ((do_create > 0) && (sta == 0)) sta = create2op(scmp, realconp, topdir); /* * If the top level repository directory is not set, then retrieve it from * the database. */ if ((tdir == NULL) && (sta == 0)) { tdir = retrieve_tdir(scmp, realconp, &sta); if (tdir == NULL) LOG(LOG_ERR, "Cannot retrieve top level repository info from DB"); } if (sta == 0) { LOG(LOG_INFO, "Top level repository directory is %s", tdir); tdirlen = strlen(tdir); } /* * Setup for actual SSL operations */ OpenSSL_add_all_algorithms(); ERR_load_crypto_strings(); LOG(LOG_NOTICE, "Rsync client session started"); if (thefile != NULL && sta == 0) { // Check that the file is in the repository, ask if not and force is // off sta = splitdf(NULL, NULL, thefile, &outdir, &outfile, &outfull); if (sta == 0) { if (strncmp(tdir, outdir, tdirlen) != 0 && force == 0) { ians = yorn("That file is not in the repository. Proceed anyway"); if (ians <= 0) sta = 1; } // if ( strstr(outdir, "TRUST") != NULL ) // trusted++; // if the user has declared it to be trusted // ask for verification unless force is set if (trusted > 0 && force == 0 && sta == 0) { ians = yorn("Really declare this file as trusted"); if (ians <= 0) sta = 1; } if (sta == 1) LOG(LOG_NOTICE, "File operation cancelled"); if (sta == 0) { LOG(LOG_INFO, "Attempting add: %s", outfile); setallowexpired(allowex); sta = add_object(scmp, realconp, outfile, outdir, outfull, trusted); if (sta < 0) { LOG(LOG_ERR, "Add failed: %s: error %s (%d)", thefile, err2string(sta), sta); if (sta == ERR_SCM_SQL) { ne = geterrorscm(realconp); if (ne != NULL && ne != 0) LOG(LOG_ERR, "\t%s", ne); } } else LOG(LOG_INFO, "Add succeeded: %s", outfile); } free((void *)outdir); free((void *)outfile); free((void *)outfull); } else LOG(LOG_ERR, "%s (%d)", err2string(sta), sta); } if (use_filelist > 0 && sta == 0) { char *line = NULL; size_t len = 0; ssize_t read; int status; setallowexpired(allowex); while ((read = getline(&line, &len, stdin)) != -1) { if (read == 0) continue; // Trim newline and skip line if empty if (line[read - 1] == '\n') line[read - 1] = '\0'; if (strlen(line) == 0) continue; // Split directory and file components of path status = splitdf(NULL, NULL, line, &outdir, &outfile, &outfull); if (status != 0) { LOG(LOG_ERR, "%s (%d)", err2string(status), status); continue; } LOG(LOG_INFO, "Attempting add: %s", outfile); // Warn if file not within repository directory if (strncmp(tdir, outdir, tdirlen) != 0) LOG(LOG_WARNING, "%s is not in the repository", line); // Add status = add_object(scmp, realconp, outfile, outdir, outfull, trusted); if (status == 0) { LOG(LOG_INFO, "Add succeeded: %s", outfile); } else { LOG(LOG_ERR, "Add failed: %s: error %s (%d)", line, err2string(status), status); if (status == ERR_SCM_SQL) { ne = geterrorscm(realconp); if (ne != NULL && ne != 0) LOG(LOG_ERR, "\t%s", ne); } } free((void *)outdir); free((void *)outfile); free((void *)outfull); } free(line); } if (thedelfile != NULL && sta == 0) { sta = splitdf(NULL, NULL, thedelfile, &outdir, &outfile, &outfull); if (sta == 0) { sta = delete_object(scmp, realconp, outfile, outdir, outfull, 0); if (sta < 0) { LOG(LOG_ERR, "Could not delete file %s: error %s (%d)", thedelfile, err2string(sta), sta); if (sta == ERR_SCM_SQL) { ne = geterrorscm(realconp); if (ne != NULL && ne != 0) LOG(LOG_ERR, "\t%s", ne); } } else LOG(LOG_INFO, "Delete operation succeeded (%s removed)", thedelfile); free((void *)outdir); free((void *)outfile); free((void *)outfull); } else LOG(LOG_ERR, "Error: %s (%d)", err2string(sta), sta); } if ((do_sockopts + do_fileopts) > 0 && sta == 0) { int protos = (-1); const int max_makesock_attempts = 10; int makesock_failures = 0; do { if (do_sockopts > 0) { uint16_t port = CONFIG_RPKI_PORT_get(); s = makesock(port, &protos); if (s < 0) { makesock_failures++; LOG(LOG_ERR, "Failed to listen on port %" PRIu16 " (failure #%d)", port, makesock_failures); sleep(1); if (makesock_failures >= max_makesock_attempts) { LOG(LOG_ERR, "%d failed attempts to create socket. Aborting.", max_makesock_attempts); sta = -1; break; } } else { makesock_failures = 0; FLUSH_LOG(); sta = sockline(scmp, realconp, s); LOG(LOG_INFO, "Socket connection closed"); FLUSH_LOG(); (void)close(s); } } if (do_fileopts > 0 && porto != NULL) { if (!isatty(0)) { LOG(LOG_DEBUG, "Opening stdin"); sfile = stdin; sta = fileline(scmp, realconp, sfile); } else { LOG(LOG_DEBUG, "Opening a socket cmdfile %s", porto); sfile = fopen(porto, "r"); if (sfile == NULL) LOG(LOG_ERR, "Could not open cmdfile"); else { sta = fileline(scmp, realconp, sfile); LOG(LOG_DEBUG, "Cmdfile closed"); (void)fclose(sfile); } } } if (sta == 0 && skifile) { LOG(LOG_DEBUG, "Starting skifile %s", skifile); sta = read_SKI_blocks(scmp, realconp, skifile); if (sta > 0) sta = 0; if (sta) LOG(LOG_ERR, "Error with skifile: %s (%d)", err2string(sta), sta); } } while (perpetual > 0); if (protos >= 0) (void)close(protos); } if (sta == 0 && skifile) { LOG(LOG_DEBUG, "Starting skifile %s", skifile); sta = read_SKI_blocks(scmp, realconp, skifile); if (sta > 0) sta = 0; if (sta) LOG(LOG_ERR, "Error with skifile: %s (%d)", err2string(sta), sta); } (void)ranlast(scmp, realconp, "RSYNC"); sqcleanup(); if (realconp != NULL) disconnectscm(realconp); freescm(scmp); if (tdir != NULL) free((void *)tdir); LOG(LOG_NOTICE, "Rsync client session ended"); config_unload(); CLOSE_LOG(); return (sta); }