Search:

SENF Extensible Network Framework

  • Home
  • Download
  • Wiki
  • BerliOS
  • ChangeLog
  • Browse SVN
  • Bug Tracker
  • Overview
  • Examples
  • HowTos
  • Glossary
  • PPI
  • Packets
  • Scheduler
  • Socket
  • Utils
  • Console
  • Daemon
  • Logger
  • Termlib
  • Main Page
  • Modules
  • Namespaces
  • Classes
  • Files
  • File List
  • File Members

Target.cc

Go to the documentation of this file.
00001 // $Id: Target.cc 1772 2011-03-10 12:45:21Z tho $
00002 //
00003 // Copyright (C) 2007
00004 // Fraunhofer (FOKUS)
00005 // Competence Center NETwork research (NET), St. Augustin, GERMANY
00006 //     Stefan Bund <g0dil@berlios.de>
00007 //
00008 // This program is free software; you can redistribute it and/or modify
00009 // it under the terms of the GNU General Public License as published by
00010 // the Free Software Foundation; either version 2 of the License, or
00011 // (at your option) any later version.
00012 //
00013 // This program is distributed in the hope that it will be useful,
00014 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00015 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016 // GNU General Public License for more details.
00017 //
00018 // You should have received a copy of the GNU General Public License
00019 // along with this program; if not, write to the
00020 // Free Software Foundation, Inc.,
00021 // 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
00022 
00026 #include "Target.hh"
00027 #include "Target.ih"
00028 
00029 // Custom includes
00030 #include <algorithm>
00031 #include <boost/format.hpp>
00032 #include "ConsoleTarget.hh"
00033 #include <senf/Utils/Console/Console.hh>
00034 
00035 //#include "Target.mpp"
00036 #define prefix_
00037 //-/////////////////////////////////////////////////////////////////////////////////////////////////
00038 
00039 //-/////////////////////////////////////////////////////////////////////////////////////////////////
00040 // senf::log::Target
00041 
00042 namespace senf {
00043 namespace log {
00044 
00045     SENF_CONSOLE_REGISTER_ENUM_MEMBER( Target, action_t, (ACCEPT)(REJECT) );
00046 
00047 namespace detail {
00048 
00049     SENF_CONSOLE_REGISTER_ENUM_MEMBER( TargetRegistry, Level,
00050                                        (VERBOSE)(NOTICE)(MESSAGE)(IMPORTANT)(CRITICAL)(FATAL) );
00051 
00052 }}}
00053 
00054 prefix_ senf::log::Target::Target(std::string const & name)
00055 {
00056     namespace kw = console::kw;
00057     namespace fty = console::factory;
00058 
00059     detail::TargetRegistry::instance().registerTarget(this, name);
00060     consoleDir_()
00061         .add("list", fty::Command(&Target::consoleList, this)
00062              .doc("Show routing table\n"
00063                   "\n"
00064                   "Columns:\n"
00065                   "    #       rule index\n"
00066                   "    STREAM  stream to match, empty to match all streams\n"
00067                   "    AREA    area to match, empty to match all targets\n"
00068                   "    LEVEL   match messages with level above this. Log levels in increasing order\n"
00069                   "            are:\n"
00070                   "                verbose, notice, message, important, critical, fatal\n"
00071                   "            If the log level is listed as 'default', the streams default limit\n"
00072                   "            applies.\n"
00073                   "    ACTION  action to take: accept or reject") );
00074     consoleDir_()
00075         .add("route", fty::Command(&Target::consoleRoute, this)
00076              .arg("index", "index at which to insert new rule")
00077              .arg("parameters", "log parameters. The log parameters select the log stream, log area\n"
00078                   "              and log level. You may specify any combination of these parameters\n"
00079                   "              in any order. Use the '/sys/log/stream' and '/sys/log/areas' commands\n"
00080                   "              to list all valid streams and areas. Valid log levels are:\n"
00081                   "                  VERBOSE NOTICE MESSAGE IMPORTANT CRITICAL FATAL")
00082              .arg("action", "routing action, one of: ACCEPT, REJECT",
00083                   kw::default_value=ACCEPT)
00084              .doc("Add routing entry. Log messages are matched against the routing table beginning\n"
00085                   "with the first entry. The action of the first matching entry determines the\n"
00086                   "handling of the message.\n"
00087                   "\n"
00088                   "Examples:\n"
00089                   "\n"
00090                   "    route ()\n"
00091                   "        route all messages with level above each streams default log limit to this\n"
00092                   "        target.\n"
00093                   "\n"
00094                   "    route 1 (my::Class VERBOSE)\n"
00095                   "        route all messages which are in the my::Class area. Insert this route after\n"
00096                   "        the first route,\n"
00097                   "\n"
00098                   "    route (senf::log::Debug VERBOSE) REJECT\n"
00099                   "    route (VERBOSE)\n"
00100                   "        route all messages not in the senf::log::Debug stream to the current area.\n"
00101                   "\n"
00102                   "The additional optional index argument identifies the position in the routing table\n"
00103                   "where the new routing entry will be added. Positive numbers count from the\n"
00104                   "beginning, 0 being the first routing entry. Negative values count from the end.") );
00105     consoleDir_()
00106         .add("route", fty::Command<void (detail::LogParameters, action_t)>(
00107                  boost::bind(&Target::consoleRoute, this, -1, _1, _2))
00108              .arg("parameters")
00109              .arg("action", kw::default_value=ACCEPT) );
00110     consoleDir_()
00111         .add("unroute",
00112              fty::Command(static_cast<void (Target::*)(int)>(&Target::unroute), this)
00113              .arg("index", "index of routing entry to remove")
00114              .overloadDoc("Remove routing entry with the given index") );
00115     consoleDir_()
00116         .add("unroute", fty::Command(&Target::consoleUnroute, this)
00117              .arg("parameters", "log parameters. The log parameters select the log stream, log area\n"
00118                   "              and log level. You may specify any combination of these parameters\n"
00119                   "              in any order. Use the '/sys/log/stream' and '/sys/log/areas' commands\n"
00120                   "              to list all valid streams and areas. Valid log levels are:\n"
00121                   "                  VERBOSE NOTICE MESSAGE IMPORTANT CRITICAL FATAL")
00122              .arg("action", "routing action, one of: ACCEPT, REJECT",
00123                   kw::default_value=ACCEPT)
00124              .overloadDoc("Remove the routing entry matching the specified arguments.") );
00125     consoleDir_()
00126         .add("flush", fty::Command(&Target::flush, this)
00127              .doc("Remove all routing entries clearing the routing table. This will disable all\n"
00128                   "logging output on this target.") );
00129 }
00130 
00131 prefix_ senf::log::Target::~Target()
00132 {
00133     while( ! rib_.empty()) {
00134         // This is slow but simplifies the area cache handling and removing a target should be
00135         // relatively seldom
00136         RIB::reverse_iterator i (rib_.rbegin());
00137         unroute(i->stream_, i->area_, i->level_, i->action_);
00138     }
00139     detail::TargetRegistry::instance().unregisterTarget(this);
00140 }
00141 
00142 prefix_ void senf::log::Target::route(std::string const & stream, std::string const & area,
00143                                       unsigned level, action_t action, int index)
00144 {
00145     detail::StreamBase const * s (0);
00146     if (! stream.empty()) {
00147         s = StreamRegistry::instance().lookup(stream);
00148         if (!s)
00149             throw InvalidStreamException();
00150     }
00151     detail::AreaBase const * a (0);
00152     if (! area.empty()) {
00153         a = AreaRegistry::instance().lookup(area);
00154         if (!a)
00155             throw InvalidAreaException();
00156     }
00157     route(s, a, level, action, index);
00158 }
00159 
00160 prefix_ void senf::log::Target::unroute(std::string const & stream, std::string const & area,
00161                                         unsigned level, action_t action)
00162 {
00163     detail::StreamBase const * s (0);
00164     if (! stream.empty()) {
00165         s = StreamRegistry::instance().lookup(stream);
00166         if (!s)
00167             throw InvalidStreamException();
00168     }
00169     detail::AreaBase const * a (0);
00170     if (! area.empty()) {
00171         a = AreaRegistry::instance().lookup(area);
00172         if (!a)
00173             throw InvalidAreaException();
00174     }
00175     unroute(s, a, level, action);
00176 }
00177 
00178 prefix_ void senf::log::Target::unroute(int index)
00179 {
00180     if (rib_.empty())
00181         return;
00182     RIB::iterator i;
00183     if (index < 0) {
00184         if (RIB::size_type(-index) >= rib_.size())
00185             i = rib_.begin();
00186         else {
00187             i = rib_.end();
00188             std::advance(i, index);
00189         }
00190     } else {
00191         if (RIB::size_type(index+1) >= rib_.size()) {
00192             i = rib_.end();
00193             --i;
00194         } else {
00195             i = rib_.begin();
00196             std::advance(i, index);
00197         }
00198     }
00199     if (i == rib_.end())
00200         return;
00201     RoutingEntry entry (*i);
00202     rib_.erase(i);
00203     if (entry.action_ == ACCEPT)
00204         updateRoutingCache(entry.stream_, entry.area_);
00205 }
00206 
00207 prefix_ void senf::log::Target::flush()
00208 {
00209     RIB old;
00210     rib_.swap(old);
00211     RIB::const_iterator i (old.begin());
00212     RIB::const_iterator const i_end (old.end());
00213     for (; i != i_end; ++i)
00214         if (i->action_ == ACCEPT)
00215             updateRoutingCache(i->stream_, i->area_);
00216 }
00217 
00218 //-/////////////////////////////////////////////////////////////////////////////////////////////////
00219 // protected members
00220 
00221 prefix_ senf::console::ScopedDirectory<> & senf::log::Target::consoleDir()
00222 {
00223     return consoleDir_();
00224 }
00225 
00226 //-/////////////////////////////////////////////////////////////////////////////////////////////////
00227 // private members
00228 
00229 prefix_ void senf::log::Target::route(detail::StreamBase const * stream,
00230                                       detail::AreaBase const * area, unsigned level,
00231                                       action_t action, int index)
00232 {
00233     RIB::iterator i;
00234     if (index < 0) {
00235         if (RIB::size_type(-index-1) >= rib_.size())
00236             i = rib_.begin();
00237         else {
00238             i = rib_.end();
00239             std::advance(i, index + 1 );
00240         }
00241     } else {
00242         if (RIB::size_type(index) >= rib_.size())
00243             i = rib_.end();
00244         else {
00245             i = rib_.begin();
00246             std::advance(i, index);
00247         }
00248     }
00249     rib_.insert(i, RoutingEntry(stream, area, level, action));
00250     if (action == ACCEPT)
00251         updateRoutingCache(stream, area);
00252     // This disables the initial fallback routing
00253     detail::TargetRegistry::instance().routed();
00254 }
00255 
00256 prefix_ void senf::log::Target::unroute(detail::StreamBase const * stream,
00257                                         detail::AreaBase const * area, unsigned level,
00258                                         action_t action)
00259 {
00260     RIB::iterator i = std::find(rib_.begin(), rib_.end(),
00261                                 RoutingEntry(stream, area, level, action));
00262     if (i != rib_.end())
00263         unroute(std::distance(rib_.begin(), i));
00264 }
00265 
00266 prefix_ void senf::log::Target::updateRoutingCache(detail::StreamBase const * stream,
00267                                                    detail::AreaBase const * area)
00268 {
00269     if (! stream) {
00270         StreamRegistry::Registry::iterator i (StreamRegistry::instance().registry_.begin());
00271         StreamRegistry::Registry::iterator const i_end (StreamRegistry::instance().registry_.end());
00272         for (; i != i_end ; ++i)
00273             updateRoutingCache(i->second, area);
00274         return;
00275     }
00276     if (! area) {
00277         AreaRegistry::Registry::iterator i (AreaRegistry::instance().registry_.begin());
00278         AreaRegistry::Registry::iterator const i_end (AreaRegistry::instance().registry_.end());
00279         for (; i != i_end ; ++i)
00280             updateRoutingCache(stream, i->second);
00281         return;
00282     }
00283     if (! area->alive())
00284         // We are globally destructing and the area is gone already ...
00285         return;
00286     unsigned limit (DISABLED::value);
00287     RIB::iterator i (rib_.begin());
00288     RIB::iterator const i_end (rib_.end());
00289     for (; i != i_end; ++i)
00290         if ( (! i->stream_ || i->stream_ == stream) &&
00291              (! i->area_ || i->area_ == area) &&
00292              i->action_ == ACCEPT ) {
00293             unsigned l (i->level_ == NONE::value ? stream->defaultRuntimeLimit() : i->level_);
00294             if (l < limit)
00295                 limit = l;
00296         }
00297     if (limit == DISABLED::value)
00298         area->removeRoutingCache(*this, *stream);
00299     else
00300         area->updateRoutingCache(*this, *stream, limit);
00301 }
00302 
00303 prefix_ void senf::log::Target::write(time_type timestamp,
00304                                       detail::StreamBase const & stream,
00305                                       detail::AreaBase const & area, unsigned level,
00306                                       std::string const & message)
00307 {
00308     RIB::iterator i (rib_.begin());
00309     RIB::iterator const i_end (rib_.end());
00310     for (; i != i_end; ++i)
00311         if ( (! i->stream_ || i->stream_ == &stream) &&
00312              (! i->area_ || i->area_ == &area) &&
00313              (i->level_ == NONE::value ? stream.defaultRuntimeLimit() : i->level_) <= level ) {
00314             if (i->action_ == ACCEPT)
00315                 v_write(timestamp, stream.v_name(), area.v_name(), level, message);
00316             return;
00317         }
00318 }
00319 
00320 namespace {
00321     std::string formatLabel(std::string const & l)
00322     {
00323         if (l.empty())
00324             return "*";
00325         if (l.size() > 29)
00326             return l.substr(l.size()-29);
00327         return l;
00328     }
00329 
00330     char const * levelNames[] = {
00331         "NONE", "VERBOSE", "NOTICE", "MESSAGE", "IMPORTANT", "CRITICAL", "FATAL", "DISABLED" };
00332 
00333     char const * levelNamesList[] = {
00334         "default", "verbose", "notice", "message", "important", "critical", "fatal", "disabled" };
00335 }
00336 
00337 prefix_ void senf::log::Target::consoleList(std::ostream & os)
00338 {
00339 
00340     boost::format fmt ("%2d %-29s %-29s %-9s %-6s\n");
00341     os << fmt % "#" % "STREAM" % "AREA" % "LEVEL" % "ACTION";
00342     unsigned n (0);
00343     for (iterator i (begin()); i != end(); ++i, ++n)
00344         os << fmt
00345             % n
00346             % formatLabel(i->stream())
00347             % formatLabel(i->area())
00348             % levelNamesList[i->level()]
00349             % (i->action() == ACCEPT ? "accept" : "reject");
00350 }
00351 
00352 prefix_ void senf::log::Target::consoleRoute(int index, detail::LogParameters const & pm, action_t action)
00353 {
00354     route(pm.stream, pm.area, pm.level, action, index);
00355 }
00356 
00357 prefix_ void senf::log::Target::consoleUnroute(detail::LogParameters const & pm, action_t action)
00358 {
00359     unroute(pm.stream, pm.area, pm.level, action);
00360 }
00361 
00362 //-/////////////////////////////////////////////////////////////////////////////////////////////////
00363 // senf::log::detail::TargetRegistry
00364 
00365 prefix_ void senf::log::detail::TargetRegistry::dynamicTarget(std::auto_ptr<Target> target)
00366 {
00367     namespace fty = console::factory;
00368 
00369     target->consoleDir()
00370         .add("remove", fty::Command<void ()>(
00371                  boost::bind(&TargetRegistry::consoleRemoveTarget, this, target.get()))
00372              .doc("Remove target.") );
00373     dynamicTargets_.insert(target.release());
00374 }
00375 
00376 prefix_ void senf::log::detail::TargetRegistry::registerTarget(Target * target,
00377                                                                std::string const & name)
00378 {
00379     targets_.insert(target);
00380     consoleDir_().add(name, target->consoleDir_());
00381 }
00382 
00383 prefix_ void senf::log::detail::TargetRegistry::write(StreamBase const & stream,
00384                                                       AreaBase const & area, unsigned level,
00385                                                       std::string const & msg)
00386 {
00387     if (fallbackRouting_) {
00388         if (level >= stream.defaultRuntimeLimit())
00389             static_cast<Target &>(ConsoleTarget::instance()).v_write(
00390                 TimeSource::now(), stream.v_name(), area.v_name(), level, msg );
00391     }
00392     else
00393         area.write( TimeSource::now(), stream, level, msg );
00394 }
00395 
00396 prefix_ void senf::log::detail::TargetRegistry::consoleRemoveTarget(Target * target)
00397 {
00398     dynamicTargets_.erase(target);
00399     delete target;
00400 }
00401 
00402 prefix_ senf::log::detail::TargetRegistry::TargetRegistry()
00403     : fallbackRouting_(true)
00404 {
00405     namespace kw = console::kw;
00406     namespace fty = console::factory;
00407 
00408     console::sysdir().add("log", consoleDir_());
00409     consoleDir_()
00410         .add("areas", fty::Command(&TargetRegistry::consoleAreas, this)
00411              .doc("List all areas") );
00412     consoleDir_()
00413         .add("streams", fty::Command(&TargetRegistry::consoleStreams, this)
00414              .doc("List all streams with the streams default runtime log level limit.") );
00415     consoleDir_()
00416         .add("message", fty::Command(&TargetRegistry::consoleWrite, this)
00417              .arg("parameters", "log parameters. The log parameters select the log stream, log area\n"
00418                   "              and log level. You may specify any combination of these parameters\n"
00419                   "              in any order. Use the '/sys/log/stream' and '/sys/log/areas' commands\n"
00420                   "              to list all valid streams and areas. Valid log levels are:\n"
00421                   "                  VERBOSE NOTICE MESSAGE IMPORTANT CRITICAL FATAL",
00422                   kw::default_value = LogParameters::defaultParameters())
00423              .arg("message", "message to write")
00424              .doc("Write log message.\n"
00425                   "\n"
00426                   "Examples:\n"
00427                   "    message \"Test\";\n"
00428                   "    message (senf::log::DefaultArea NOTICE) \"Test notice\";\n"
00429                   "    message (FATAL) \"Program on fire\";\n"
00430                   "    message (VERBOSE senf::log::Debug) \"Debug message\";") );
00431     consoleDir_()
00432         .add("self", fty::Command(&TargetRegistry::consoleSelf, this)
00433              .doc("Get the log directory of the current network client. Example usage:\n"
00434                   "\n"
00435                   "Just get the log config directory\n"
00436                   "    $ /sys/log/self\n"
00437                   "    <Directory '/sys/log/client-xxx.xxx.xxx.xxx:xxx'>\n"
00438                   "\n"
00439                   "Route all messages to the currently connected client\n"
00440                   "    $ /sys/log/self { route () ); }") );
00441 }
00442 
00443 prefix_ senf::log::detail::TargetRegistry::~TargetRegistry()
00444 {
00445     Targets::iterator i (dynamicTargets_.begin());
00446     Targets::iterator const i_end (dynamicTargets_.end());
00447     for (; i != i_end; ++i)
00448         delete *i;
00449 }
00450 
00451 prefix_ void senf::log::detail::TargetRegistry::consoleAreas(std::ostream & os)
00452 {
00453     AreaRegistry::iterator i (AreaRegistry::instance().begin());
00454     AreaRegistry::iterator const i_end (AreaRegistry::instance().end());
00455     for (; i != i_end; ++i)
00456         os << *i << "\n";
00457 }
00458 
00459 prefix_ void senf::log::detail::TargetRegistry::consoleStreams(std::ostream & os)
00460 {
00461     StreamRegistry::iterator i (StreamRegistry::instance().begin());
00462     StreamRegistry::iterator const i_end (StreamRegistry::instance().end());
00463     for (; i != i_end; ++i) {
00464         os << *i << " "
00465            << levelNames[StreamRegistry::instance().lookup(*i)->defaultRuntimeLimit()] << "\n";
00466     }
00467 }
00468 
00469 prefix_ void senf::log::detail::TargetRegistry::consoleWrite(LogParameters pm,
00470                                                              std::string const & msg)
00471 {
00472     pm.setDefaults();
00473     write(*pm.stream, *pm.area, pm.level, msg);
00474 }
00475 
00476 prefix_ boost::shared_ptr<senf::console::DirectoryNode>
00477 senf::log::detail::TargetRegistry::consoleSelf(std::ostream & os)
00478 {
00479     return senf::console::Client::get(os).consoleDir().node().thisptr();
00480 }
00481 
00482 //-/////////////////////////////////////////////////////////////////////////////////////////////////
00483 // senf::log::detail::LogParameters
00484 
00485 prefix_ void senf::log::detail::LogParameters::clear()
00486 {
00487     stream = 0;
00488     area = 0;
00489     level = NONE::value;
00490 }
00491 
00492 prefix_ void senf::log::detail::LogParameters::setDefaults()
00493 {
00494     if (! stream)
00495         stream = & senf::log::Debug::instance();
00496     if (! area)
00497         area = & senf::log::DefaultArea::instance();
00498     if (level == NONE::value)
00499         level = MESSAGE::value;
00500 }
00501 
00502 prefix_ senf::log::detail::LogParameters senf::log::detail::LogParameters::defaultParameters()
00503 {
00504     LogParameters pm;
00505     pm.clear();
00506     pm.setDefaults();
00507     return pm;
00508 }
00509 
00510 //-/////////////////////////////////////////////////////////////////////////////////////////////////
00511 // namespace members
00512 
00513 prefix_ std::ostream & senf::log::operator<<(std::ostream & os, senf::log::Target::action_t const & action)
00514 {
00515     if (action == Target::ACCEPT) os << "ACCEPT";
00516     else if (action == Target::REJECT) os << "REJECT";
00517     else os << "unknown action";
00518     return os;
00519 }
00520 
00521 namespace {
00522 
00523     void parseParamToken(std::string const & value, senf::log::detail::LogParameters & out)
00524     {
00525         senf::log::detail::StreamBase const * s (
00526             senf::log::StreamRegistry::instance().lookup(value));
00527         if (s) {
00528             if (out.stream)
00529                 throw senf::console::SyntaxErrorException("duplicate stream parameter");
00530             out.stream = s;
00531             return;
00532         }
00533 
00534         senf::log::detail::AreaBase const * a (
00535             senf::log::AreaRegistry::instance().lookup(value));
00536         if (a) {
00537             if (out.area)
00538                 throw senf::console::SyntaxErrorException("duplicate area parameter");
00539             out.area = a;
00540             return;
00541         }
00542 
00543         char const ** i (
00544             std::find(levelNames+1, levelNames+sizeof(levelNames)/sizeof(levelNames[0])-1, value));
00545         if (i == levelNames+sizeof(levelNames)/sizeof(levelNames[0])-1)
00546             throw senf::console::SyntaxErrorException("invalid log parameter");
00547         if (out.level != senf::log::NONE::value)
00548             throw senf::console::SyntaxErrorException("duplicate level parameter");
00549         out.level = i-levelNames;
00550     }
00551 
00552 }
00553 
00554 prefix_ std::ostream & senf::log::detail::operator<<(std::ostream & os,
00555                                                      LogParameters const & pm)
00556 {
00557     os << '(';
00558     if (pm.stream)
00559         os << pm.stream->v_name();
00560     if (pm.area)
00561         if (pm.stream) os << ' ';
00562         os << pm.area->v_name();
00563     if (pm.level != NONE::value)
00564         if (pm.stream || pm.area) os << ' ';
00565         os << levelNames[pm.level];
00566     os << ')';
00567     return os;
00568 }
00569 
00570 prefix_ void senf::log::detail::
00571 senf_console_parse_argument(console::ParseCommandInfo::TokensRange const & tokens,
00572                             LogParameters & out)
00573 {
00574     out.clear();
00575 
00576     for (console::ParseCommandInfo::TokensRange::iterator i (tokens.begin());
00577          i != tokens.end(); ++i)
00578         parseParamToken(i->value(), out);
00579 }
00580 
00581 //-/////////////////////////////////////////////////////////////////////////////////////////////////
00582 // I need to put this here, otherwise the file target will not be registered
00583 // if it is not used ... :-(
00584 
00585 senf::log::FileTarget::RegisterConsole senf::log::FileTarget::RegisterConsole::instance;
00586 senf::log::SyslogTarget::RegisterConsole senf::log::SyslogTarget::RegisterConsole::instance;
00587 senf::log::SyslogUDPTarget::RegisterConsole senf::log::SyslogUDPTarget::RegisterConsole::instance;
00588 
00589 //-/////////////////////////////////////////////////////////////////////////////////////////////////
00590 #undef prefix_
00591 //#include "Target.mpp"
00592 
00593 
00594 // Local Variables:
00595 // mode: c++
00596 // fill-column: 100
00597 // comment-column: 40
00598 // c-file-style: "senf"
00599 // indent-tabs-mode: nil
00600 // ispell-local-dictionary: "american"
00601 // compile-command: "scons -u test"
00602 // End:

Contact: senf-dev@lists.berlios.de | © 2006-2010 Fraunhofer Institute for Open Communication Systems, Network Research