Commit 4c3627e3 authored by Tien-Thinh Nguyen's avatar Tien-Thinh Nguyen

add config file

parent 76066640
# prompt has been removed for easier Ctrl+C Ctrl+V
# please update the following information according to your configuration
INSTANCE=1
PREFIX='/usr/local/etc/oai'
sudo mkdir -m 0777 -p $PREFIX
cp ../../etc/smf.conf $PREFIX
declare -A SMF_CONF
SMF_CONF[@INSTANCE@]=$INSTANCE
SMF_CONF[@PREFIX@]=$PREFIX
SMF_CONF[@PID_DIRECTORY@]='/var/run'
SMF_CONF[@SMF_INTERFACE_NAME_FOR_N4@]='ens3'
SMF_CONF[@SMF_INTERFACE_NAME_FOR_SBI@]='ens3'
SMF_CONF[@SMF_INTERFACE_PORT_FOR_SBI@]='80'
SMF_CONF[@SMF_INTERFACE_HTTP2_PORT_FOR_SBI@]='9090'
SMF_CONF[@SMF_API_VERSION@]='v1'
SMF_CONF[@UDM_IPV4_ADDRESS@]='172.16.1.103'
SMF_CONF[@UDM_PORT@]='80'
SMF_CONF[@UDM_API_VERSION@]='v2'
SMF_CONF[@AMF_IPV4_ADDRESS@]='192.168.122.183'
SMF_CONF[@AMF_PORT@]='80'
SMF_CONF[@AMF_API_VERSION@]='v1'
SMF_CONF[@UPF_IPV4_ADDRESS@]='192.168.122.151'
SMF_CONF[@DEFAULT_DNS_IPV4_ADDRESS@]='8.8.8.8'
SMF_CONF[@DEFAULT_DNS_SEC_IPV4_ADDRESS@]='4.4.4.4'
for K in "${!SMF_CONF[@]}"; do
egrep -lRZ "$K" $PREFIX | xargs -0 -l sed -i -e "s|$K|${SMF_CONF[$K]}|g"
ret=$?;[[ ret -ne 0 ]] && echo "Tried to replace $K with ${SMF_CONF[$K]}"
done
# prompt has been removed for easier Ctrl+C Ctrl+V
sudo ifconfig eno1:smf 172.16.1.101 up # SMF
sudo ifconfig eno1:amf 172.16.1.102 up # AMF
sudo ifconfig eno1:udm 172.16.1.103 up # UDM
sudo ifconfig eno1:sn4 172.16.2.101 up # SMF N4 interface
sudo ifconfig eno1:un4 172.16.2.102 up # UPF N4 interface
INSTANCE=1
PREFIX='/usr/local/etc/oai'
sudo mkdir -m 0777 -p $PREFIX
cp ../../etc/smf.conf $PREFIX
declare -A SMF_CONF
SMF_CONF[@INSTANCE@]=$INSTANCE
SMF_CONF[@PREFIX@]=$PREFIX
SMF_CONF[@PID_DIRECTORY@]='/var/run'
SMF_CONF[@SMF_INTERFACE_NAME_FOR_N4@]='eno1:sn4'
SMF_CONF[@SMF_INTERFACE_NAME_FOR_SBI@]='eno1:smf'
SMF_CONF[@SMF_INTERFACE_IPV4_ADDRESS_FOR_SBI@]='172.16.1.101'
SMF_CONF[@SMF_INTERFACE_PORT_FOR_SBI@]='80'
SMF_CONF[@SMF_INTERFACE_HTTP2_PORT_FOR_SBI@]='9090'
SMF_CONF[@UDM_IPV4_ADDRESS@]='172.16.1.103'
SMF_CONF[@UDM_PORT@]='80'
SMF_CONF[@AMF_IPV4_ADDRESS@]='172.16.1.102'
SMF_CONF[@AMF_PORT@]='80'
SMF_CONF[@UPF_IPV4_ADDRESS@]='172.16.2.102'
SMF_CONF[@DEFAULT_DNS_IPV4_ADDRESS@]='8.8.8.8'
SMF_CONF[@DEFAULT_DNS_SEC_IPV4_ADDRESS@]='4.4.4.4'
for K in "${!SMF_CONF[@]}"; do
egrep -lRZ "$K" $PREFIX | xargs -0 -l sed -i -e "s|$K|${SMF_CONF[$K]}|g"
ret=$?;[[ ret -ne 0 ]] && echo "Tried to replace $K with ${SMF_CONF[$K]}"
done
......@@ -57,9 +57,9 @@ void Logger::_init(const char *app, const bool log_stdout,
m_async_cmd = new _Logger("async_c", m_sinks, ss.str().c_str());
m_itti = new _Logger("itti ", m_sinks, ss.str().c_str());
m_smf_app = new _Logger("smf_app", m_sinks, ss.str().c_str());
m_nrf_app = new _Logger("nrf_app", m_sinks, ss.str().c_str());
m_system = new _Logger("system ", m_sinks, ss.str().c_str());
m_smf_n11 = new _Logger("smf_n11", m_sinks, ss.str().c_str());
m_nrf_n11 = new _Logger("nrf_n11", m_sinks, ss.str().c_str());
m_nrf_sbi = new _Logger("sbi_srv", m_sinks, ss.str().c_str());
}
......
......@@ -92,14 +92,14 @@ class Logger {
static _Logger& itti() {
return *singleton().m_itti;
}
static _Logger& smf_app() {
return *singleton().m_smf_app;
static _Logger& nrf_app() {
return *singleton().m_nrf_app;
}
static _Logger& system() {
return *singleton().m_system;
}
static _Logger& smf_n11() {
return *singleton().m_smf_n11;
static _Logger& nrf_n11() {
return *singleton().m_nrf_n11;
}
static _Logger& nrf_sbi() {
return *singleton().m_nrf_sbi;
......@@ -126,9 +126,9 @@ class Logger {
_Logger *m_async_cmd;
_Logger *m_itti;
_Logger *m_smf_app;
_Logger *m_nrf_app;
_Logger *m_system;
_Logger *m_smf_n11;
_Logger *m_nrf_n11;
_Logger *m_nrf_sbi;
};
......
......@@ -25,7 +25,7 @@
bool mime_parser::parse(const std::string &str) {
std::string CRLF = "\r\n";
Logger::smf_app().debug("Parsing the message with Simple Parser");
Logger::nrf_app().debug("Parsing the message with Simple Parser");
//find boundary
std::size_t content_type_pos = str.find("Content-Type"); //first part
......@@ -33,7 +33,7 @@ bool mime_parser::parse(const std::string &str) {
return false;
std::string boundary_str = str.substr(2, content_type_pos - 4); // 2 for -- and 2 for CRLF
Logger::smf_app().debug("Boundary: %s", boundary_str.c_str());
Logger::nrf_app().debug("Boundary: %s", boundary_str.c_str());
std::string boundary_full = "--" + boundary_str + CRLF;
std::string last_boundary = "--" + boundary_str + "--" + CRLF;
......@@ -50,7 +50,7 @@ bool mime_parser::parse(const std::string &str) {
break;
p.content_type = str.substr(content_type_pos + 14,
crlf_pos - (content_type_pos + 14));
Logger::smf_app().debug("Content Type: %s", p.content_type.c_str());
Logger::nrf_app().debug("Content Type: %s", p.content_type.c_str());
crlf_pos = str.find(CRLF + CRLF, content_type_pos); //beginning of content
boundary_pos = str.find(boundary_full, crlf_pos);
......@@ -59,7 +59,7 @@ bool mime_parser::parse(const std::string &str) {
}
if (boundary_pos > 0) {
p.body = str.substr(crlf_pos + 4, boundary_pos - 2 - (crlf_pos + 4));
Logger::smf_app().debug("Body: %s", p.body.c_str());
Logger::nrf_app().debug("Body: %s", p.body.c_str());
mime_parts.push_back(p);
}
}
......@@ -82,9 +82,9 @@ unsigned char* mime_parser::format_string_as_hex(const std::string &str) {
unsigned char *data_hex = (uint8_t*) malloc(str_len / 2 + 1);
conv::ascii_to_hex(data_hex, (const char*) data);
Logger::smf_app().debug("[Format string as Hex] Input string (%d bytes): %s ",
Logger::nrf_app().debug("[Format string as Hex] Input string (%d bytes): %s ",
str_len, str.c_str());
Logger::smf_app().debug("Data (formatted):");
Logger::nrf_app().debug("Data (formatted):");
#if DEBUG_IS_ON
for (int i = 0; i < str_len / 2; i++)
printf(" %02x ", data_hex[i]);
......
......@@ -81,13 +81,13 @@ bool util::is_pid_file_lock_success(const char *pid_file_name) {
O_RDWR | O_CREAT,
S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); /* Read/write by owner, read by grp, others */
if (0 > g_fd_pid_file) {
Logger::smf_app().error("open filename %s failed %d:%s\n", pid_file_name,
Logger::nrf_app().error("open filename %s failed %d:%s\n", pid_file_name,
errno, strerror(errno));
return false;
}
if (0 > util::lockfile(g_fd_pid_file, F_TLOCK)) {
Logger::smf_app().error("lockfile filename %s failed %d:%s\n",
Logger::nrf_app().error("lockfile filename %s failed %d:%s\n",
pid_file_name, errno, strerror(errno));
if ( EACCES == errno || EAGAIN == errno) {
close(g_fd_pid_file);
......@@ -96,7 +96,7 @@ bool util::is_pid_file_lock_success(const char *pid_file_name) {
}
// fruncate file content
if (ftruncate(g_fd_pid_file, 0)) {
Logger::smf_app().error("truncate %s failed %d:%s\n", pid_file_name, errno,
Logger::nrf_app().error("truncate %s failed %d:%s\n", pid_file_name, errno,
strerror(errno));
close(g_fd_pid_file);
return false;
......@@ -105,7 +105,7 @@ bool util::is_pid_file_lock_success(const char *pid_file_name) {
g_pid = getpid();
snprintf(pid_dec, 64 /* should be big enough */, "%ld", (long) g_pid);
if ((ssize_t) -1 == write(g_fd_pid_file, pid_dec, strlen(pid_dec))) {
Logger::smf_app().error("write PID to filename %s failed %d:%s\n",
Logger::nrf_app().error("write PID to filename %s failed %d:%s\n",
pid_file_name, errno, strerror(errno));
return false;
}
......
......@@ -19,7 +19,7 @@
* contact@openairinterface.org
*/
/*! \file smf_config.cpp
/*! \file nrf_config.cpp
\brief
\author Lionel GAUTHIER, Tien-Thinh NGUYEN
\company Eurecom
......@@ -32,7 +32,7 @@
#include <cstdlib>
#include <iomanip>
#include <iostream>
//#include "string.hpp"
#include "string.hpp"
// C includes
#include <arpa/inet.h>
......@@ -46,11 +46,155 @@
#include <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string/split.hpp>
#include "common_defs.h"
#include "if.hpp"
#include "logger.hpp"
#include "nrf_app.hpp"
using namespace std;
//using namespace libconfig;
using namespace libconfig;
using namespace oai::nrf;
//extern smf_config smf_cfg;
extern nrf_config nrf_cfg;
//------------------------------------------------------------------------------
int nrf_config::load_interface(const Setting &if_cfg, interface_cfg_t &cfg) {
if_cfg.lookupValue(NRF_CONFIG_STRING_INTERFACE_NAME, cfg.if_name);
util::trim(cfg.if_name);
if (not boost::iequals(cfg.if_name, "none")) {
std::string address = { };
if_cfg.lookupValue(NRF_CONFIG_STRING_IPV4_ADDRESS, address);
util::trim(address);
if (boost::iequals(address, "read")) {
if (get_inet_addr_infos_from_iface(cfg.if_name, cfg.addr4, cfg.network4,
cfg.mtu)) {
Logger::nrf_app().error(
"Could not read %s network interface configuration", cfg.if_name);
return RETURNerror ;
}
} else {
std::vector<std::string> words;
boost::split(words, address, boost::is_any_of("/"),
boost::token_compress_on);
if (words.size() != 2) {
Logger::nrf_app().error(
"Bad value " NRF_CONFIG_STRING_IPV4_ADDRESS " = %s in config file",
address.c_str());
return RETURNerror ;
}
unsigned char buf_in_addr[sizeof(struct in6_addr)]; // you never know...
if (inet_pton(AF_INET, util::trim(words.at(0)).c_str(), buf_in_addr)
== 1) {
memcpy(&cfg.addr4, buf_in_addr, sizeof(struct in_addr));
} else {
Logger::nrf_app().error(
"In conversion: Bad value " NRF_CONFIG_STRING_IPV4_ADDRESS " = %s in config file",
util::trim(words.at(0)).c_str());
return RETURNerror ;
}
cfg.network4.s_addr = htons(
ntohs(cfg.addr4.s_addr)
& 0xFFFFFFFF << (32 - std::stoi(util::trim(words.at(1)))));
}
if_cfg.lookupValue(NRF_CONFIG_STRING_PORT, cfg.port);
}
return RETURNok ;
}
//------------------------------------------------------------------------------
int nrf_config::load(const string &config_file) {
Config cfg;
unsigned char buf_in6_addr[sizeof(struct in6_addr)];
// Read the file. If there is an error, report it and exit.
try {
cfg.readFile(config_file.c_str());
} catch (const FileIOException &fioex) {
Logger::nrf_app().error("I/O error while reading file %s - %s",
config_file.c_str(), fioex.what());
throw;
} catch (const ParseException &pex) {
Logger::nrf_app().error("Parse error at %s:%d - %s", pex.getFile(),
pex.getLine(), pex.getError());
throw;
}
const Setting &root = cfg.getRoot();
try {
const Setting &nrf_cfg = root[NRF_CONFIG_STRING_NRF_CONFIG];
} catch (const SettingNotFoundException &nfex) {
Logger::nrf_app().error("%s : %s", nfex.what(), nfex.getPath());
return RETURNerror ;
}
const Setting &nrf_cfg = root[NRF_CONFIG_STRING_NRF_CONFIG];
try {
nrf_cfg.lookupValue(NRF_CONFIG_STRING_INSTANCE, instance);
} catch (const SettingNotFoundException &nfex) {
Logger::nrf_app().info("%s : %s, using defaults", nfex.what(),
nfex.getPath());
}
try {
nrf_cfg.lookupValue(NRF_CONFIG_STRING_PID_DIRECTORY, pid_dir);
} catch (const SettingNotFoundException &nfex) {
Logger::nrf_app().info("%s : %s, using defaults", nfex.what(),
nfex.getPath());
}
try {
const Setting &sbi_cfg = nrf_cfg[NRF_CONFIG_STRING_INTERFACE_SBI];
load_interface(sbi_cfg, sbi);
//HTTP2 port
if (!(sbi_cfg.lookupValue(NRF_CONFIG_STRING_SBI_HTTP2_PORT, sbi_http2_port))) {
Logger::nrf_app().error(NRF_CONFIG_STRING_SBI_HTTP2_PORT "failed");
throw(NRF_CONFIG_STRING_SBI_HTTP2_PORT "failed");
}
//SBI API VERSION
if (!(sbi_cfg.lookupValue(NRF_CONFIG_STRING_API_VERSION, sbi_api_version))) {
Logger::nrf_app().error(NRF_CONFIG_STRING_API_VERSION "failed");
throw(NRF_CONFIG_STRING_API_VERSION "failed");
}
} catch (const SettingNotFoundException &nfex) {
Logger::nrf_app().error("%s : %s", nfex.what(), nfex.getPath());
return RETURNerror ;
}
return true;
}
//------------------------------------------------------------------------------
void nrf_config::display() {
Logger::nrf_app().info("==== EURECOM %s v%s ====", PACKAGE_NAME,
PACKAGE_VERSION);
Logger::nrf_app().info("Configuration NRF:");
Logger::nrf_app().info("- Instance ..............: %d\n", instance);
Logger::nrf_app().info("- PID dir ...............: %s\n", pid_dir.c_str());
Logger::nrf_app().info("- SBI Networking:");
Logger::nrf_app().info(" Interface name ......: %s", sbi.if_name.c_str());
Logger::nrf_app().info(" IPv4 Addr ...........: %s",
inet_ntoa(sbi.addr4));
Logger::nrf_app().info(" Port ................: %d", sbi.port);
Logger::nrf_app().info(" HTTP2 port ..........: %d", sbi_http2_port);
Logger::nrf_app().info(" API version..........: %s", sbi_api_version.c_str());
}
//------------------------------------------------------------------------------
nrf_config::~nrf_config() {
}
......@@ -19,7 +19,7 @@
* contact@openairinterface.org
*/
/*! \file smf_config.hpp
/*! \file nrf_config.hpp
* \brief
\author Lionel GAUTHIER, Tien-Thinh NGUYEN
\company Eurecom
......@@ -39,9 +39,70 @@
#include <vector>
//#include "thread_sched.hpp"
namespace oai{
#define NRF_CONFIG_STRING_NRF_CONFIG "NRF"
#define NRF_CONFIG_STRING_PID_DIRECTORY "PID_DIRECTORY"
#define NRF_CONFIG_STRING_INSTANCE "INSTANCE"
#define NRF_CONFIG_STRING_INTERFACE_NAME "INTERFACE_NAME"
#define NRF_CONFIG_STRING_IPV4_ADDRESS "IPV4_ADDRESS"
#define NRF_CONFIG_STRING_PORT "PORT"
#define NRF_CONFIG_STRING_INTERFACE_SBI "SBI"
#define NRF_CONFIG_STRING_SBI_HTTP2_PORT "HTTP2_PORT"
#define NRF_CONFIG_STRING_API_VERSION "API_VERSION"
namespace oai {
namespace nrf {
typedef struct interface_cfg_s {
std::string if_name;
struct in_addr addr4;
struct in_addr network4;
struct in6_addr addr6;
unsigned int mtu;
unsigned int port;
} interface_cfg_t;
class nrf_config {
private:
int load_interface(const libconfig::Setting &if_cfg, interface_cfg_t &cfg);
public:
/* Reader/writer lock for this configuration */
std::mutex m_rw_lock;
std::string pid_dir;
unsigned int instance = 0;
interface_cfg_t sbi;
unsigned int sbi_http2_port;
std::string sbi_api_version;
//Local configuration
bool local_configuration;
nrf_config()
:
m_rw_lock(),
pid_dir(),
instance(0),
sbi() {
sbi.port = 80;
sbi_http2_port = 8080;
sbi_api_version = "v1";
}
;
~nrf_config();
void lock() {
m_rw_lock.lock();
}
;
void unlock() {
m_rw_lock.unlock();
}
;
int load(const std::string &config_file);
void display();
};
} // namespace nrf
}
......
......@@ -18,11 +18,10 @@
#include "options.hpp"
//#include "smf-api-server.h"
#include "nrf-api-server.h"
#include "pistache/endpoint.h"
#include "pistache/http.h"
#include "pistache/router.h"
//#include "smf-http2-server.h"
#include <iostream>
#include <thread>
......@@ -33,10 +32,13 @@
#include "logger.hpp"
//using namespace nrf;
using namespace oai::nrf;
//using namespace util;
using namespace std;
//using namespace oai::smf_server::api;
//using namespace oai::nrf_server::api;
nrf_app *nrf_app_inst = nullptr;
nrf_config nrf_cfg;
void send_heartbeat_to_tasks(const uint32_t sequence);
......@@ -48,7 +50,7 @@ void send_heartbeat_to_tasks(const uint32_t sequence)
std::shared_ptr<itti_msg_ping> i = std::shared_ptr<itti_msg_ping>(itti_msg);
int ret = itti_inst->send_broadcast_msg(i);
if (RETURNok != ret) {
Logger::smf_app().error( "Could not send ITTI message %s to task TASK_ALL", i->get_msg_name());
Logger::nrf_app().error( "Could not send ITTI message %s to task TASK_ALL", i->get_msg_name());
}
*/
}
......@@ -64,20 +66,20 @@ void my_app_signal_handler(int s)
std::cout << "Freeing Allocated memory..." << std::endl;
if (async_shell_cmd_inst) delete async_shell_cmd_inst; async_shell_cmd_inst = nullptr;
std::cout << "Async Shell CMD memory done." << std::endl;
if (smf_api_server_1) {
smf_api_server_1->shutdown();
delete smf_api_server_1;
smf_api_server_1 = nullptr;
if (nrf_api_server_1) {
nrf_api_server_1->shutdown();
delete nrf_api_server_1;
nrf_api_server_1 = nullptr;
}
if (smf_api_server_2) {
smf_api_server_2->stop();
delete smf_api_server_2;
smf_api_server_2 = nullptr;
if (nrf_api_server_2) {
nrf_api_server_2->stop();
delete nrf_api_server_2;
nrf_api_server_2 = nullptr;
}
std::cout << "SMF API Server memory done." << std::endl;
if (itti_inst) delete itti_inst; itti_inst = nullptr;
std::cout << "ITTI memory done." << std::endl;
if (smf_app_inst) delete smf_app_inst; smf_app_inst = nullptr;
if (nrf_app_inst) delete nrf_app_inst; nrf_app_inst = nullptr;
std::cout << "SMF APP memory done." << std::endl;
std::cout << "Freeing Allocated memory done" << std::endl;
*/
......@@ -96,9 +98,9 @@ int main(int argc, char **argv)
}
// Logger
Logger::init( "smf" , Options::getlogStdout() , Options::getlogRotFilelog());
Logger::init( "nrf" , Options::getlogStdout() , Options::getlogRotFilelog());
Logger::smf_app().startup( "Options parsed" );
Logger::nrf_app().startup( "Options parsed" );
struct sigaction sigIntHandler;
sigIntHandler.sa_handler = my_app_signal_handler;
......@@ -107,44 +109,39 @@ int main(int argc, char **argv)
sigaction(SIGINT, &sigIntHandler, NULL);
// Config
// smf_cfg.load(Options::getlibconfigConfig());
// smf_cfg.display();
nrf_cfg.load(Options::getlibconfigConfig());
nrf_cfg.display();
// Inter task Interface
// itti_inst = new itti_mw();
// itti_inst->start(smf_cfg.itti.itti_timer_sched_params);
// itti_inst->start(nrf_cfg.itti.itti_timer_sched_params);
// system command
// async_shell_cmd_inst = new async_shell_cmd(smf_cfg.itti.async_cmd_sched_params);
// async_shell_cmd_inst = new async_shell_cmd(nrf_cfg.itti.async_cmd_sched_params);
// SMF application layer
// smf_app_inst = new smf_app(Options::getlibconfigConfig());
// nrf_app_inst = new nrf_app(Options::getlibconfigConfig());
// PID file
// Currently hard-coded value. TODO: add as config option.
/* string pid_file_name = get_exe_absolute_path("/var/run", smf_cfg.instance);
/* string pid_file_name = get_exe_absolute_path("/var/run", nrf_cfg.instance);
if (! is_pid_file_lock_success(pid_file_name.c_str())) {
Logger::smf_app().error( "Lock PID file %s failed\n", pid_file_name.c_str());
Logger::nrf_app().error( "Lock PID file %s failed\n", pid_file_name.c_str());
exit (-EDEADLK);
}
*/
/*
//SMF Pistache API server (HTTP1)
Pistache::Address addr(std::string(inet_ntoa (*((struct in_addr *)&smf_cfg.sbi.addr4))) , Pistache::Port(smf_cfg.sbi.port));
smf_api_server_1 = new SMFApiServer(addr, smf_app_inst);
smf_api_server_1->init(2);
//smf_api_server_1->start();
std::thread smf_http1_manager(&SMFApiServer::start, smf_api_server_1);
//SMF NGHTTP API server (HTTP2)
smf_api_server_2 = new smf_http2_server(conv::toString(smf_cfg.sbi.addr4), smf_cfg.sbi_http2_port, smf_app_inst);
//smf_api_server_2->start();
std::thread smf_http2_manager(&smf_http2_server::start, smf_api_server_2);
smf_http1_manager.join();
smf_http2_manager.join();
Pistache::Address addr(std::string(inet_ntoa (*((struct in_addr *)&nrf_cfg.sbi.addr4))) , Pistache::Port(nrf_cfg.sbi.port));
nrf_api_server_1 = new SMFApiServer(addr, nrf_app_inst);
nrf_api_server_1->init(2);
//nrf_api_server_1->start();
std::thread nrf_http1_manager(&SMFApiServer::start, nrf_api_server_1);
nrf_http1_manager.join();
*/
/*
FILE *fp = NULL;
std::string filename = fmt::format("/tmp/smf_{}.status", getpid());
std::string filename = fmt::format("/tmp/nrf_{}.status", getpid());
fp = fopen(filename.c_str(), "w+");
fprintf(fp, "STARTED\n");
fflush(fp);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment