/*
* click-install.cc -- configuration installer for Click kernel module
* Eddie Kohler
*
* Copyright (c) 1999-2000 Massachusetts Institute of Technology
* Copyright (c) 2000 Mazu Networks, Inc.
* Copyright (c) 2002 International Computer Science Institute
* Copyright (c) 2006 Regents of the University of California
*
* 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 "common.hh"
#include "routert.hh"
#include "lexert.hh"
#include <click/error.hh>
#include <click/confparse.hh>
#include <click/clp.h>
#include <click/straccum.hh>
#include <click/driver.hh>
#include "toolutils.hh"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#if FOR_BSDMODULE
# include <sys/param.h>
# include <sys/mount.h>
#elif FOR_LINUXMODULE
# include <sys/mount.h>
#endif
#include <fcntl.h>
#include <unistd.h>
#define HELP_OPT 300
#define VERSION_OPT 301
#define CLICKPATH_OPT 302
#define ROUTER_OPT 303
#define EXPRESSION_OPT 304
#define UNINSTALL_OPT 305
#define HOTSWAP_OPT 306
#define MAP_OPT 307
#define VERBOSE_OPT 308
#define THREADS_OPT 309
#define PRIVATE_OPT 310
#define PRIORITY_OPT 311
#define GREEDY_OPT 312
static Clp_Option options[] = {
{ "cabalistic", 0, PRIVATE_OPT, 0, Clp_Negate },
{ "clickpath", 'C', CLICKPATH_OPT, Clp_ArgString, 0 },
{ "expression", 'e', EXPRESSION_OPT, Clp_ArgString, 0 },
{ "file", 'f', ROUTER_OPT, Clp_ArgString, 0 },
{ "help", 0, HELP_OPT, 0, 0 },
{ "hot-swap", 'h', HOTSWAP_OPT, 0, Clp_Negate },
{ "hotswap", 'h', HOTSWAP_OPT, 0, Clp_Negate },
{ "priority", 'n', PRIORITY_OPT, Clp_ArgInt, 0 },
#if FOR_LINUXMODULE
{ "map", 'm', MAP_OPT, 0, 0 },
{ "private", 'p', PRIVATE_OPT, 0, Clp_Negate },
{ "threads", 't', THREADS_OPT, Clp_ArgUnsigned, 0 },
{ "greedy", 'G', GREEDY_OPT, 0, Clp_Negate },
#endif
{ "uninstall", 'u', UNINSTALL_OPT, 0, Clp_Negate },
{ "verbose", 'V', VERBOSE_OPT, 0, Clp_Negate },
{ "version", 'v', VERSION_OPT, 0, 0 },
};
static const char *program_name;
#if FOR_LINUXMODULE
static bool output_map;
#endif
static String::Initializer string_initializer;
static String tmpdir;
static String click_buildtool_prog;
void
short_usage()
{
fprintf(stderr, "Usage: %s [OPTION]... [ROUTERFILE]\n\
Try '%s --help' for more information.\n",
program_name, program_name);
}
void
usage()
{
printf("\
'Click-install' installs a kernel Click configuration. It loads the Click\n\
kernel module, and any other necessary modules, as required.\n\
\n\
Usage: %s [OPTION]... [ROUTERFILE]\n\
\n\
Options:\n\
-f, --file FILE Read router configuration from FILE.\n\
-e, --expression EXPR Use EXPR as router configuration.\n\
-h, --hot-swap Hot-swap install new configuration.\n\
-u, --uninstall Uninstall Click from kernel, then reinstall.\n\
-n, --priority N Set kernel thread priority to N (lower is better).\n", program_name);
#if FOR_LINUXMODULE
printf("\
-p, --private Make /proc/click readable only by root.\n\
-t, --threads N Use N threads (multithreaded Click only).\n\
-G, --greedy Make Click thread take up an entire CPU.\n");
# if HAVE_LINUXMODULE_2_6
printf("\
-m, --map Print load map to the standard output.\n");
# endif
#endif
printf("\
-V, --verbose Print information about files installed.\n\
-C, --clickpath PATH Use PATH for CLICKPATH.\n\
--help Print this message and exit.\n\
-v, --version Print version number and exit.\n\
\n\
Report bugs to <click@pdos.lcs.mit.edu>.\n");
}
static void
prepare_tmpdir(RouterT *r, ErrorHandler *errh)
{
ContextErrorHandler cerrh(errh, "While preparing to compile packages:");
BailErrorHandler berrh(&cerrh);
// change to temporary directory
tmpdir = click_mktmpdir(&berrh);
assert(tmpdir);
if (chdir(tmpdir.c_str()) < 0)
berrh.fatal("cannot chdir to %s: %s", tmpdir.c_str(), strerror(errno));
// find compile program
click_buildtool_prog = clickpath_find_file("click-buildtool", "bin", CLICK_BINDIR, &cerrh);
assert(click_buildtool_prog);
// look for .hh files
if (r) {
const Vector<ArchiveElement> &archive = r->archive();
for (int i = 0; i < archive.size(); i++)
if (archive[i].name.substring(-3) == ".hh") {
String filename = archive[i].name;
FILE *f = fopen(filename.c_str(), "w");
if (!f)
cerrh.warning("%s: %s", filename.c_str(), strerror(errno));
else {
fwrite(archive[i].data.data(), 1, archive[i].data.length(), f);
fclose(f);
}
}
}
}
static void
compile_archive_packages(RouterT *r, HashMap<String, int> &packages,
ErrorHandler *errh)
{
Vector<String> requirements = r->requirements();
// go over requirements
for (int i = 0; i < requirements.size(); i++) {
const String &req = requirements[i];
// skip if already have object file
if (r->archive_index(req + OBJSUFFIX) >= 0
|| packages[req] >= 0)
continue;
// look for source file, prepare temporary directory
int source_ae = r->archive_index(req + CXXSUFFIX);
if (source_ae < 0)
source_ae = r->archive_index(req + ".cc");
if (source_ae < 0)
continue;
if (!tmpdir)
prepare_tmpdir(r, errh);
// found source file, so compile it
ArchiveElement ae = r->archive(source_ae);
errh->message("Compiling package %s from config archive", ae.name.c_str());
ContextErrorHandler cerrh
(errh, "While compiling package '" + req + OBJSUFFIX "':");
// write .cc file
String filename = req + "_.cc";
String source_text = ae.data;
FILE *f = fopen(filename.c_str(), "w");
if (!f)
cerrh.fatal("%s: %s", filename.c_str(), strerror(errno));
fwrite(source_text.data(), 1, source_text.length(), f);
fclose(f);
// run click-compile
StringAccum compile_command;
compile_command << click_buildtool_prog << " makepackage -C "
<< tmpdir << " -t " COMPILETARGET " "
<< req << " " << req << "_.cc 1>&2";
int compile_retval = system(compile_command.c_str());
if (compile_retval == 127)
cerrh.fatal("could not run '%s'", compile_command.c_str());
else if (compile_retval < 0)
cerrh.fatal("could not run '%s': %s", compile_command.c_str(), strerror(errno));
else if (compile_retval != 0)
cerrh.fatal("'%s' failed", compile_command.c_str());
// grab object file and add to archive
ArchiveElement obj_ae = init_archive_element(req + OBJSUFFIX, 0600);
obj_ae.data = file_string(req + OBJSUFFIX, &cerrh);
r->add_archive(obj_ae);
}
}
static void
install_module(const String &filename, const String &options,
ErrorHandler *errh)
{
#if FOR_LINUXMODULE
String cmdline = "/sbin/insmod ";
if (output_map)
cmdline += "-m ";
cmdline += filename;
if (options)
cmdline += " " + options;
int retval = system(cmdline.c_str());
if (retval != 0)
errh->fatal("'%s' failed", cmdline.c_str());
#else
String cmdline = "/sbin/kldload " + filename;
assert(!options);
int retval = system(cmdline.c_str());
if (retval != 0)
errh->fatal("'%s' failed", cmdline.c_str());
#endif
}
static void
install_required_packages(RouterT *r, HashMap<String, int> &packages,
HashMap<String, int> &active_modules,
ErrorHandler *errh)
{
// check for uncompiled archive packages and try to compile them
compile_archive_packages(r, packages, errh);
Vector<String> requirements = r->requirements();
// go over requirements
for (int i = 0; i < requirements.size(); i++) {
String req = requirements[i];
// look for object in archive
int obj_aei = r->archive_index(req + OBJSUFFIX);
if (obj_aei >= 0 && packages[req] < 0) {
// install archived objects. mark them with leading underscores.
// may require renaming to avoid clashes in 'insmod'
// choose module name
String insmod_name = req + OBJSUFFIX;
if (verbose)
errh->message("Installing package %s (%s" OBJSUFFIX " from config archive)", insmod_name.c_str(), req.c_str());
// install module
if (!tmpdir)
prepare_tmpdir(0, errh);
const ArchiveElement &ae = r->archive(obj_aei);
String tmpnam = tmpdir + insmod_name;
FILE *f = fopen(tmpnam.c_str(), "w");
if (!f)
errh->fatal("%s: %s", tmpnam.c_str(), strerror(errno));
fwrite(ae.data.data(), 1, ae.data.length(), f);
fclose(f);
install_module(tmpnam, String(), errh);
// cleanup
packages.insert(req, 1);
active_modules.insert(insmod_name, 1);
} else if (packages[req] < 0) {
// install required package from CLICKPATH
String filename = req + OBJSUFFIX;
String pathname = clickpath_find_file(filename, "lib", CLICK_LIBDIR);
if (!pathname) {
filename = req + ".o";
pathname = clickpath_find_file(filename, "lib", CLICK_LIBDIR);
if (!pathname)
errh->fatal("cannot find required package '%s" OBJSUFFIX "'\nin CLICKPATH or '%s'", req.c_str(), CLICK_LIBDIR);
}
// install module
if (verbose)
errh->message("Installing package %s (%s)", req.c_str(), pathname.c_str());
install_module(pathname, String(), errh);
packages.insert(req, 1);
active_modules.insert(filename, 1);
} else {
// package already loaded; note in 'active_modules' that we still need
// it
if (verbose)
errh->message("Not installing package %s, version already exists", req.c_str());
String filename = req;
if (active_modules[filename] < 0)
filename = req + OBJSUFFIX;
if (active_modules[filename] < 0)
filename = req + ".o";
if (active_modules[filename] == 0)
active_modules.insert(filename, 1);
}
}
}
int
main(int argc, char **argv)
{
click_static_initialize();
CLICK_DEFAULT_PROVIDES;
ErrorHandler *nop_errh = ErrorHandler::default_handler();
ErrorHandler *errh = new PrefixErrorHandler(nop_errh, "click-install: ");
// read command line arguments
Clp_Parser *clp =
Clp_NewParser(argc, argv, sizeof(options) / sizeof(options[0]), options);
Clp_SetOptionChar(clp, '+', Clp_ShortNegated);
program_name = Clp_ProgramName(clp);
const char *router_file = 0;
bool file_is_expr = false;
bool uninstall = false;
bool hotswap = false;
int priority = -100;
#if FOR_LINUXMODULE
bool accessible = true;
int threads = 1;
bool greedy = false;
output_map = false;
#endif
while (1) {
int opt = Clp_Next(clp);
switch (opt) {
case HELP_OPT:
usage();
exit(0);
break;
case VERSION_OPT:
printf("click-install (Click) %s\n", CLICK_VERSION);
printf("Click packages in %s, binaries in %s\n", CLICK_LIBDIR, CLICK_BINDIR);
printf("Copyright (c) 1999-2000 Massachusetts Institute of Technology\n\
Copyright (c) 2000-2005 Mazu Networks, Inc.\n\
Copyright (c) 2002 International Computer Science Institute\n\
This is free software; see the source for copying conditions.\n\
There is NO warranty, not even for merchantability or fitness for a\n\
particular purpose.\n");
exit(0);
break;
case CLICKPATH_OPT:
set_clickpath(clp->arg);
break;
case ROUTER_OPT:
case EXPRESSION_OPT:
case Clp_NotOption:
if (router_file) {
errh->error("router configuration specified twice");
goto bad_option;
}
router_file = clp->arg;
file_is_expr = (opt == EXPRESSION_OPT);
break;
#if FOR_LINUXMODULE
case THREADS_OPT:
threads = clp->val.u;
if (threads < 1) {
errh->error("must have at least one thread");
goto bad_option;
}
break;
case PRIVATE_OPT:
accessible = clp->negated;
break;
case PRIORITY_OPT:
priority = clp->val.i;
break;
case MAP_OPT:
# if HAVE_LINUXMODULE_2_6
errh->warning("'%s' ignored on 2.6 kernels", Clp_CurOptionName(clp));
# else
output_map = !clp->negated;
# endif
break;
case GREEDY_OPT:
greedy = !clp->negated;
break;
#endif
case UNINSTALL_OPT:
uninstall = !clp->negated;
break;
case HOTSWAP_OPT:
hotswap = !clp->negated;
break;
case VERBOSE_OPT:
verbose = !clp->negated;
break;
bad_option:
case Clp_BadOption:
short_usage();
exit(1);
break;
case Clp_Done:
goto done;
}
}
done:
// check options
if (hotswap && uninstall)
errh->warning("'--hotswap' and '--uninstall' are mutually exclusive");
RouterT *r = read_router(router_file, file_is_expr, nop_errh);
if (r)
r->flatten(nop_errh);
if (!r || errh->nerrors() > 0)
exit(1);
// pathnames of important Click files
String clickfs_config = clickfs_prefix + String("/config");
String clickfs_hotconfig = clickfs_prefix + String("/hotconfig");
String clickfs_errors = clickfs_prefix + String("/errors");
String clickfs_packages = clickfs_prefix + String("/packages");
String clickfs_priority = clickfs_prefix + String("/priority");
// uninstall Click if requested
if (uninstall)
unload_click(errh);
// install Click module if required
if (access(clickfs_packages.c_str(), F_OK) < 0) {
#if FOR_LINUXMODULE
// find and install proclikefs.o
StringMap modules(-1);
if (read_active_modules(modules, errh) && modules["proclikefs"] < 0) {
# if HAVE_LINUXMODULE_2_6
String proclikefs_o =
clickpath_find_file("proclikefs.ko", "lib", CLICK_LIBDIR, errh);
# else
String proclikefs_o =
clickpath_find_file("proclikefs.o", "lib", CLICK_LIBDIR, errh);
# endif
if (verbose)
errh->message("Installing proclikefs (%s)", proclikefs_o.c_str());
install_module(proclikefs_o, String(), errh);
}
#endif
// find loadable module
#if FOR_BSDMODULE || (FOR_LINUXMODULE && HAVE_LINUXMODULE_2_6)
String click_o =
clickpath_find_file("click.ko", "lib", CLICK_LIBDIR, errh);
#elif FOR_LINUXMODULE
String click_o =
clickpath_find_file("click.o", "lib", CLICK_LIBDIR, errh);
#endif
if (verbose)
errh->message("Installing Click module (%s)", click_o.c_str());
// install it in the kernel
#if FOR_LINUXMODULE
String options;
if (threads > 1)
options += "threads=" + String(threads);
if (greedy)
options += " greedy=1";
if (!accessible)
options += " accessible=0";
install_module(click_o, options, errh);
#elif FOR_BSDMODULE
install_module(click_o, String(), errh);
#endif
#if FOR_BSDMODULE || FOR_LINUXMODULE
// make clickfs_prefix directory if required
if (access(clickfs_prefix, F_OK) < 0 && errno == ENOENT) {
if (mkdir(clickfs_prefix, 0777) < 0)
errh->fatal("cannot make directory %s: %s", clickfs_prefix, strerror(errno));
}
// mount Click file system
if (verbose)
errh->message("Mounting Click module at %s", clickfs_prefix);
# if FOR_BSDMODULE
int mount_retval = mount("click", clickfs_prefix, 0, 0);
# else
int mount_retval = mount("none", clickfs_prefix, "click", 0, 0);
# endif
if (mount_retval < 0 && (verbose || errno != EBUSY))
errh->error("cannot mount %s: %s", clickfs_prefix, strerror(errno));
#endif
// check that all is well
if (access(clickfs_packages.c_str(), F_OK) < 0)
errh->fatal("cannot install Click module");
} else {
#if FOR_LINUXMODULE
if (threads > 1)
errh->warning("Click module already installed, '--threads' ignored");
#endif
}
// find current packages
HashMap<String, int> active_modules(-1);
HashMap<String, int> packages(-1);
read_active_modules(active_modules, errh);
read_package_file(clickfs_packages, packages, errh);
// install required packages
install_required_packages(r, packages, active_modules, errh);
// set priority
if (priority > -100) {
FILE *f = fopen(clickfs_priority.c_str(), "w");
if (!f)
errh->fatal("%s: %s", clickfs_priority.c_str(), strerror(errno));
fprintf(f, "%d\n", priority);
fclose(f);
}
// write flattened configuration to CLICKFS/config
int exit_status = 0;
{
String config_place = (hotswap ? clickfs_hotconfig : clickfs_config);
if (verbose)
errh->message("Writing configuration to %s", config_place.c_str());
int fd = open(config_place.c_str(), O_WRONLY | O_TRUNC);
if (fd < 0)
errh->fatal("cannot install configuration: %s", strerror(errno));
// XXX include packages?
String config = r->configuration_string();
int pos = 0;
while (pos < config.length()) {
ssize_t written = write(fd, config.data() + pos, config.length() - pos);
if (written >= 0)
pos += written;
else if (errno != EAGAIN && errno != EINTR)
errh->fatal("%s: %s", config_place.c_str(), strerror(errno));
}
int retval = close(fd);
if (retval < 0 && errno == EINVAL)
exit_status = 2;
else if (retval < 0)
errh->error("%s: %s", config_place.c_str(), strerror(errno));
}
// report errors
{
char buf[1024];
int fd = open(clickfs_errors.c_str(), O_RDONLY | O_NONBLOCK);
if (fd < 0)
errh->warning("%s: %s", clickfs_errors.c_str(), strerror(errno));
else {
if (verbose)
errh->message("Waiting for errors");
while (1) {
struct timeval wait;
wait.tv_sec = 0;
wait.tv_usec = 50000;
(void) select(0, 0, 0, 0, &wait);
ssize_t got = read(fd, buf, 1024);
if (got > 0)
fwrite(buf, 1, got, stderr);
else if (got == 0)
break;
else if (errno != EINTR && errno != EAGAIN) {
errh->error("%s: %s", clickfs_errors.c_str(), strerror(errno));
break;
}
}
close(fd);
}
}
// remove unused packages
remove_unneeded_packages(active_modules, packages, errh);
if (verbose)
errh->message("Done");
exit(exit_status);
}
syntax highlighted by Code2HTML, v. 0.9.1