// -*- c-basic-offset: 4; related-file-name: "../include/click/driver.hh" -*- /* * driver.cc -- support for packages * Eddie Kohler * * Copyright (c) 2001 Mazu Networks, Inc. * Copyright (c) 2003 International Computer Science Institute * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, subject to the conditions * listed in the Click LICENSE file. These conditions include: you must * preserve this copyright notice, and you cannot mention the copyright * holders in advertising related to the Software without their permission. * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This * notice is a summary of the Click LICENSE file; the license in that file is * legally binding. */ #include #include #if CLICK_LINUXMODULE # define WANT_MOD_USE_COUNT 1 /* glue.hh should not define MOD_USE_COUNTs */ #endif #include #include #include #include #if !CLICK_LINUXMODULE && !CLICK_BSDMODULE # include # include # include # include # include # include #endif #if CLICK_TOOL # include "lexert.hh" # include "routert.hh" # include #else # include #endif #if CLICK_USERLEVEL # include # include # include # include # include #endif #if HAVE_DYNAMIC_LINKING && !CLICK_LINUXMODULE && !CLICK_BSDMODULE # define CLICK_PACKAGE_LOADED 1 #endif // GENERIC PACKAGE SUPPORT struct ClickProvision { CLICK_NAME(String) name; #if CLICK_PACKAGE_LOADED bool loaded : 1; #endif int provided; }; static int nprovisions; static int provisions_cap; static ClickProvision *provisions; static ClickProvision * find_provision(const CLICK_NAME(String) &name, int add) { ClickProvision *pf = 0; for (int i = 0; i < nprovisions; i++) if (provisions[i].name == name) return &provisions[i]; else if (add && provisions[i].provided == 0 #if CLICK_PACKAGE_LOADED && !provisions[i].loaded #endif ) pf = &provisions[i]; if (!add) return 0; // otherwise, create new ClickProvision if (!pf) { if (nprovisions >= provisions_cap) { int n = (nprovisions ? nprovisions * 2 : 4); ClickProvision *npf = new ClickProvision[n]; if (!npf) return 0; for (int i = 0; i < nprovisions; i++) npf[i] = provisions[i]; provisions_cap = n; delete[] provisions; provisions = npf; } pf = &provisions[nprovisions++]; } pf->name = name; #if CLICK_PACKAGE_LOADED pf->loaded = false; #endif pf->provided = 0; return pf; } extern "C" void click_provide(const char *package) { ClickProvision *p = find_provision(package, 1); if (p) p->provided++; } extern "C" void click_unprovide(const char *package) { ClickProvision *p = find_provision(package, 0); if (p && p->provided > 0) p->provided--; } extern "C" bool click_has_provision(const char *package) { ClickProvision *p = find_provision(package, 0); return (p && p->provided > 0); } extern "C" void click_public_packages(CLICK_NAME(Vector) &v) { for (int i = 0; i < nprovisions; i++) if (provisions[i].provided > 0 && provisions[i].name && provisions[i].name[0] != '@') v.push_back(provisions[i].name); } #if CLICK_LINUXMODULE || CLICK_BSDMODULE extern "C" void click_cleanup_packages() { delete[] provisions; provisions = 0; nprovisions = provisions_cap = 0; } #endif #if CLICK_USERLEVEL || (HAVE_DYNAMIC_LINKING && !CLICK_LINUXMODULE && !CLICK_BSDMODULE) CLICK_DECLS static int archive_index(const Vector *archive, const String &what) { if (archive) for (int i = 0; i < archive->size(); i++) if (archive->at(i).name == what) return i; return -1; } CLICK_ENDDECLS #endif #if CLICK_PACKAGE_LOADED CLICK_DECLS static String *click_buildtool_prog, *tmpdir; static bool check_tmpdir(const Vector *archive, ErrorHandler *errh) { // change to temporary directory if (!tmpdir) tmpdir = new String(click_mktmpdir(errh)); if (!*tmpdir) return *tmpdir; // find compile program if (!click_buildtool_prog) click_buildtool_prog = new String(clickpath_find_file("click-buildtool", "bin", CLICK_BINDIR, errh)); if (!*click_buildtool_prog) return *click_buildtool_prog; // store .hh files in temporary directory if (archive) for (int i = 0; i < archive->size(); i++) if ((*archive)[i].name.substring(-3) == ".hh") { String filename = *tmpdir + (*archive)[i].name; FILE *f = fopen(filename.c_str(), "w"); if (!f) errh->warning("%s: %s", filename.c_str(), strerror(errno)); else { fwrite((*archive)[i].data.data(), 1, (*archive)[i].data.length(), f); fclose(f); } } return *tmpdir; } static String compile_archive_file(String package, const Vector *archive, int ai, ErrorHandler *errh) { if (!check_tmpdir(archive, errh)) return String(); #ifdef CLICK_TOOL String package_file = package + ".to"; String target = "tool"; #else String package_file = package + ".uo"; String target = "userlevel"; #endif ContextErrorHandler cerrh (errh, "While compiling package '" + package_file + "':"); // write .cc file const ArchiveElement &ae = archive->at(ai); String filename = ae.name; int rightdot = filename.find_right('.'); if (rightdot >= 0 && filename.substring(0, rightdot) == package) filename = package + "_" + filename.substring(rightdot); String filepath = *tmpdir + filename; FILE *f = fopen(filepath.c_str(), "w"); if (!f) { cerrh.error("%s: %s", filepath.c_str(), strerror(errno)); return String(); } fwrite(ae.data.data(), 1, ae.data.length(), f); fclose(f); // run click-compile StringAccum compile_command; compile_command << *click_buildtool_prog << " makepackage -q -C " << *tmpdir << " -t " << target << " " << package << " " << filename << " 1>&2"; errh->message("%s", compile_command.c_str()); int compile_retval = system(compile_command.c_str()); if (compile_retval == 127) cerrh.error("could not run '%s'", compile_command.c_str()); else if (compile_retval < 0) cerrh.error("could not run '%s': %s", compile_command.c_str(), strerror(errno)); else if (compile_retval != 0) cerrh.error("'%s' failed", compile_command.c_str()); else return *tmpdir + package_file; return String(); } void clickdl_load_requirement(String name, const Vector *archive, ErrorHandler *errh) { ClickProvision *p = find_provision(name, 1); if (!p || p->loaded) return; ContextErrorHandler cerrh(errh, "While loading package '" + name + "':"); #ifdef CLICK_TOOL String suffix = ".to", cxx_suffix = ".t.cc"; #else String suffix = ".uo", cxx_suffix = ".u.cc"; #endif String package; // check archive int ai; if ((ai = archive_index(archive, name + suffix)) >= 0) { if (!check_tmpdir(archive, &cerrh)) return; package = *tmpdir + "/" + name + suffix; FILE *f = fopen(package.c_str(), "wb"); if (!f) { cerrh.error("cannot open '%s': %s", package.c_str(), strerror(errno)); package = String(); } else { const ArchiveElement &ae = archive->at(ai); fwrite(ae.data.data(), 1, ae.data.length(), f); fclose(f); } } else if ((ai = archive_index(archive, name + cxx_suffix)) >= 0) package = compile_archive_file(name, archive, ai, &cerrh); else if ((ai = archive_index(archive, name + ".cc")) >= 0) package = compile_archive_file(name, archive, ai, &cerrh); else { // search path package = clickpath_find_file(name + suffix, "lib", CLICK_LIBDIR); if (!package) package = clickpath_find_file(name + ".o", "lib", CLICK_LIBDIR); if (!package) cerrh.error("can't find required package '%s%s'\nin CLICKPATH or '%s'", name.c_str(), suffix.c_str(), CLICK_LIBDIR); } p->loaded = true; if (package) clickdl_load_package(package, &cerrh); } CLICK_ENDDECLS #endif /* CLICK_PACKAGE_LOADED */ #ifdef CLICK_USERLEVEL extern void click_export_elements(); extern void click_unexport_elements(); CLICK_DECLS namespace { class RequireLexerExtra : public LexerExtra { public: RequireLexerExtra(const Vector *a) : _archive(a) { } void require(String, ErrorHandler *); private: const Vector *_archive; }; void RequireLexerExtra::require(String name, ErrorHandler *errh) { # ifdef HAVE_DYNAMIC_LINKING if (!click_has_provision(name.c_str())) clickdl_load_requirement(name, _archive, errh); # endif if (!click_has_provision(name.c_str())) errh->error("requirement '%s' not available", name.c_str()); } } static Lexer *click_lexer; extern "C" int click_add_element_type(const char *ename, Element *(*func)(uintptr_t), uintptr_t thunk) { assert(ename); if (!click_lexer && !(click_lexer = new Lexer)) return -99; else return click_lexer->add_element_type(ename, func, thunk); } extern "C" void click_remove_element_type(int which) { if (click_lexer) click_lexer->remove_element_type(which); } enum { GH_CLASSES, GH_PACKAGES }; static String read_handler(Element *, void *thunk) { Vector v; switch (reinterpret_cast(thunk)) { case GH_CLASSES: if (click_lexer) click_lexer->element_type_names(v); break; case GH_PACKAGES: click_public_packages(v); break; default: return "\n"; } StringAccum sa; for (int i = 0; i < v.size(); i++) sa << v[i] << '\n'; return sa.take_string(); } void click_static_initialize() { String::static_initialize(); NameInfo::static_initialize(); cp_va_static_initialize(); ErrorHandler::static_initialize(new FileErrorHandler(stderr, "")); Router::static_initialize(); NotifierSignal::static_initialize(); CLICK_DEFAULT_PROVIDES; Router::add_read_handler(0, "classes", read_handler, (void *)GH_CLASSES); Router::add_read_handler(0, "packages", read_handler, (void *)GH_PACKAGES); click_export_elements(); } void click_static_cleanup() { delete click_lexer; click_lexer = 0; click_unexport_elements(); Router::static_cleanup(); ErrorHandler::static_cleanup(); cp_va_static_cleanup(); NameInfo::static_cleanup(); HashMap_ArenaFactory::static_cleanup(); # ifdef HAVE_DYNAMIC_LINKING delete tmpdir; delete click_buildtool_prog; tmpdir = click_buildtool_prog = 0; # endif /* HAVE_DYNAMIC_LINKING */ String::static_cleanup(); } Router * click_read_router(String filename, bool is_expr, ErrorHandler *errh, bool initialize, Master *master) { if (!errh) errh = ErrorHandler::silent_handler(); int before = errh->nerrors(); // read file String config_str; if (is_expr) { config_str = filename; filename = ""; } else { config_str = file_string(filename, errh); if (!filename || filename == "-") filename = ""; } if (errh->nerrors() > before) return 0; // find config string in archive Vector archive; if (config_str.length() != 0 && config_str[0] == '!') { separate_ar_string(config_str, archive, errh); int i = archive_index(&archive, "config"); if (i >= 0) config_str = archive[i].data; else { errh->error("%s: archive has no 'config' section", filename.c_str()); return 0; } } // lex if (!click_lexer) click_lexer = new Lexer(); RequireLexerExtra lextra(&archive); int cookie = click_lexer->begin_parse(config_str, filename, &lextra, errh); while (click_lexer->ystatement()) /* do nothing */; Router *router = click_lexer->create_router(master ? master : new Master(1)); click_lexer->end_parse(cookie); // initialize if requested if (initialize) if (errh->nerrors() > before || router->initialize(errh) < 0) { delete router; return 0; } return router; } CLICK_ENDDECLS #endif /* CLICK_USERLEVEL */ #if CLICK_TOOL CLICK_DECLS void click_static_initialize() { String::static_initialize(); cp_va_static_initialize(); ErrorHandler::static_initialize(new FileErrorHandler(stderr, "")); } CLICK_ENDDECLS #endif