// -*- 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 <click/config.h>
#include <click/pathvars.h>
#if CLICK_LINUXMODULE
# define WANT_MOD_USE_COUNT 1 /* glue.hh should not define MOD_USE_COUNTs */
#endif
#include <click/driver.hh>
#include <click/package.hh>
#include <click/hashmap.hh>
#include <click/error.hh>
#if !CLICK_LINUXMODULE && !CLICK_BSDMODULE
# include <click/userutils.hh>
# include <click/straccum.hh>
# include <unistd.h>
# include <errno.h>
# include <string.h>
# include <stdlib.h>
#endif
#if CLICK_TOOL
# include "lexert.hh"
# include "routert.hh"
# include <click/confparse.hh>
#else
# include <click/lexer.hh>
#endif
#if CLICK_USERLEVEL
# include <click/master.hh>
# include <click/notifier.hh>
# include <click/straccum.hh>
# include <click/nameinfo.hh>
# include <click/bighashmap_arena.hh>
#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)<CLICK_NAME(String)> &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<ArchiveElement> *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<ArchiveElement> *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<ArchiveElement> *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<ArchiveElement> *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<ArchiveElement> *a) : _archive(a) { }
void require(String, ErrorHandler *);
private:
const Vector<ArchiveElement> *_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<String> v;
switch (reinterpret_cast<intptr_t>(thunk)) {
case GH_CLASSES:
if (click_lexer)
click_lexer->element_type_names(v);
break;
case GH_PACKAGES:
click_public_packages(v);
break;
default:
return "<error>\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 = "<expr>";
} else {
config_str = file_string(filename, errh);
if (!filename || filename == "-")
filename = "<stdin>";
}
if (errh->nerrors() > before)
return 0;
// find config string in archive
Vector<ArchiveElement> 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
syntax highlighted by Code2HTML, v. 0.9.1