aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMicael Karlberg <[email protected]>2018-06-08 15:25:51 +0200
committerMicael Karlberg <[email protected]>2018-09-18 14:42:46 +0200
commit5b7ac9423f07171e4cd682a216cc86b1d32c660a (patch)
treef1b68e5162f18b5191c63fd6ac46f0109f240538
parent6b2c750c53a288d999c0f63dc6fe26a22d63ed00 (diff)
downloadotp-5b7ac9423f07171e4cd682a216cc86b1d32c660a.tar.gz
otp-5b7ac9423f07171e4cd682a216cc86b1d32c660a.tar.bz2
otp-5b7ac9423f07171e4cd682a216cc86b1d32c660a.zip
[net-nif] The net-module now actually loads
The net (nif) module now actually loads (automatically) when the VM is started (*on linux*). Now we must make sure it *actually* works, and implement the rest of the stuff...
-rw-r--r--erts/emulator/nifs/common/net_nif.c149
-rw-r--r--erts/preloaded/ebin/init.beambin51508 -> 51544 bytes
-rw-r--r--erts/preloaded/ebin/net.beambin0 -> 4792 bytes
-rw-r--r--erts/preloaded/src/init.erl1
-rw-r--r--erts/preloaded/src/net.erl52
5 files changed, 135 insertions, 67 deletions
diff --git a/erts/emulator/nifs/common/net_nif.c b/erts/emulator/nifs/common/net_nif.c
index b8fa6628a7..edcf944c66 100644
--- a/erts/emulator/nifs/common/net_nif.c
+++ b/erts/emulator/nifs/common/net_nif.c
@@ -24,6 +24,8 @@
*
*/
+#define STATIC_ERLANG_NIF 1
+
/* #include <stdio.h> */
/* #include <stdlib.h> */
/* #include <stdarg.h> */
@@ -262,6 +264,8 @@ typedef unsigned long long llu_t;
#define MKT2(E,E1,E2) enif_make_tuple2((E), (E1), (E2))
#define MKT3(E,E1,E2,E3) enif_make_tuple3((E), (E1), (E2), (E3))
#define MKT4(E,E1,E2,E3,E4) enif_make_tuple4((E), (E1), (E2), (E3), (E4))
+#define MKT5(E,E1,E2,E3,E4,E5) \
+ enif_make_tuple5((E), (E1), (E2), (E3), (E4), (E5))
#define MKT8(E,E1,E2,E3,E4,E5,E6,E7,E8) \
enif_make_tuple8((E), (E1), (E2), (E3), (E4), (E5), (E6), (E7), (E8))
@@ -364,8 +368,11 @@ static ERL_NIF_TERM nif_if_names(ErlNifEnv* env,
static ERL_NIF_TERM ngetnameinfo(ErlNifEnv* env,
const SockAddress* saP,
- socklen_t saLen,
+ SOCKLEN_T saLen,
int flags);
+static ERL_NIF_TERM ngetaddrinfo(ErlNifEnv* env,
+ char* host,
+ char* serv);
static ERL_NIF_TERM nif_name2index(ErlNifEnv* env,
char* ifn);
static ERL_NIF_TERM nif_index2name(ErlNifEnv* env,
@@ -387,16 +394,16 @@ static void net_down(ErlNifEnv* env,
static BOOLEAN_T decode_in_sockaddr(ErlNifEnv* env,
const ERL_NIF_TERM eAddr,
SockAddress* saP,
- socklen_t* saLen);
+ SOCKLEN_T* saLen);
static BOOLEAN_T decode_in4_sockaddr(ErlNifEnv* env,
const ERL_NIF_TERM* addrt,
SockAddress* saP,
- socklen_t* saLen);
+ SOCKLEN_T* saLen);
#if defined(HAVE_IN6) && defined(AF_INET6)
static BOOLEAN_T decode_in6_sockaddr(ErlNifEnv* env,
const ERL_NIF_TERM* addrt,
SockAddress* saP,
- socklen_t* saLen);
+ SOCKLEN_T* saLen);
#endif
static BOOLEAN_T decode_nameinfo_flags(ErlNifEnv* env,
const ERL_NIF_TERM eflags,
@@ -404,10 +411,26 @@ static BOOLEAN_T decode_nameinfo_flags(ErlNifEnv* env,
static BOOLEAN_T decode_nameinfo_flags_list(ErlNifEnv* env,
const ERL_NIF_TERM eflags,
int* flags);
+static
+BOOLEAN_T decode_addrinfo_string(ErlNifEnv* env,
+ const ERL_NIF_TERM eString,
+ char** stringP);
static ERL_NIF_TERM encode_address_info(ErlNifEnv* env,
struct addrinfo* addrInfo);
static unsigned int address_info_length(struct addrinfo* addrInfoP);
+static ERL_NIF_TERM make_address_info(ErlNifEnv* env,
+ struct addrinfo* addrInfoP);
+static ERL_NIF_TERM make_addrinfo_family(ErlNifEnv* env,
+ int family);
+static ERL_NIF_TERM make_addrinfo_type(ErlNifEnv* env,
+ int socktype);
+static ERL_NIF_TERM make_addrinfo_proto(ErlNifEnv* env,
+ int proto);
+static ERL_NIF_TERM make_addrinfo_addr(ErlNifEnv* env,
+ struct sockaddr* addrP,
+ SOCKLEN_T addrLen);
+
static ERL_NIF_TERM make_ok2(ErlNifEnv* env, ERL_NIF_TERM val);
// static ERL_NIF_TERM make_ok3(ErlNifEnv* env, ERL_NIF_TERM val1, ERL_NIF_TERM val2);
static ERL_NIF_TERM make_error(ErlNifEnv* env, ERL_NIF_TERM reason);
@@ -448,18 +471,25 @@ static const struct in6_addr in6addr_loopback =
/* *** String constants *** */
+static char str_address_info[] = "address_info";
static char str_dgram[] = "dgram";
static char str_error[] = "error";
static char str_idn[] = "idn";
static char str_idna_allow_unassigned[] = "idna_allow_unassigned";
static char str_idna_use_std3_ascii_rules[] = "idna_use_std3_ascii_rules";
+static char str_inet[] = "inet";
+static char str_inet6[] = "inet6";
+static char str_ip[] = "ip";
static char str_namereqd[] = "namereqd";
static char str_name_info[] = "name_info";
static char str_nofqdn[] = "nofqdn";
static char str_numerichost[] = "numerichost";
static char str_numericserv[] = "numericserv";
static char str_ok[] = "ok";
+static char str_stream[] = "stream";
+static char str_tcp[] = "tcp";
static char str_true[] = "true";
+static char str_udp[] = "udp";
static char str_undefined[] = "undefined";
// static char str_lowdelay[] = "lowdelay";
@@ -469,6 +499,7 @@ static char str_undefined[] = "undefined";
/* (special) error string constants */
// static char str_eafnosupport[] = "eafnosupport";
+static char str_eaddrfamily[] = "eaddrfamily";
static char str_eagain[] = "eagain";
static char str_ebadflags[] = "ebadflags";
static char str_efail[] = "efail";
@@ -476,10 +507,14 @@ static char str_efamily[] = "efamily";
static char str_einval[] = "einval";
// static char str_eisconn[] = "eisconn";
static char str_emem[] = "emem";
+static char str_enodata[] = "enodata";
static char str_enoname[] = "enoname";
// static char str_enotclosing[] = "enotclosing";
// static char str_enotconn[] = "enotconn";
static char str_eoverflow[] = "eoverflow";
+static char str_eservice[] = "eservice";
+static char str_esocktype[] = "esocktype";
+static char str_esystem[] = "esystem";
// static char str_exalloc[] = "exalloc";
// static char str_exbadstate[] = "exbadstate";
// static char str_exbusy[] = "exbusy";
@@ -490,11 +525,15 @@ static char str_eoverflow[] = "eoverflow";
/* *** Atoms *** */
-static ERL_NIF_TERM atom_error;
+static ERL_NIF_TERM atom_address_info;
static ERL_NIF_TERM atom_dgram;
+static ERL_NIF_TERM atom_error;
static ERL_NIF_TERM atom_idn;
static ERL_NIF_TERM atom_idna_allow_unassigned;
static ERL_NIF_TERM atom_idna_use_std3_ascii_rules;
+static ERL_NIF_TERM atom_inet;
+static ERL_NIF_TERM atom_inet6;
+static ERL_NIF_TERM atom_ip;
static ERL_NIF_TERM atom_namereqd;
static ERL_NIF_TERM atom_name_info;
static ERL_NIF_TERM atom_nofqdn;
@@ -502,8 +541,11 @@ static ERL_NIF_TERM atom_numerichost;
static ERL_NIF_TERM atom_numericserv;
static ERL_NIF_TERM atom_ok;
// static ERL_NIF_TERM atom_select;
+static ERL_NIF_TERM atom_stream;
// static ERL_NIF_TERM atom_timeout;
+static ERL_NIF_TERM atom_tcp;
static ERL_NIF_TERM atom_true;
+static ERL_NIF_TERM atom_udp;
static ERL_NIF_TERM atom_undefined;
// static ERL_NIF_TERM atom_lowdelay;
@@ -512,6 +554,7 @@ static ERL_NIF_TERM atom_undefined;
// static ERL_NIF_TERM atom_mincost;
// static ERL_NIF_TERM atom_eafnosupport;
+static ERL_NIF_TERM atom_eaddrfamily;
static ERL_NIF_TERM atom_eagain;
static ERL_NIF_TERM atom_ebadflags;
static ERL_NIF_TERM atom_efail;
@@ -519,10 +562,14 @@ static ERL_NIF_TERM atom_efamily;
static ERL_NIF_TERM atom_einval;
// static ERL_NIF_TERM atom_eisconn;
static ERL_NIF_TERM atom_emem;
+static ERL_NIF_TERM atom_enodata;
static ERL_NIF_TERM atom_enoname;
// static ERL_NIF_TERM atom_enotclosing;
// static ERL_NIF_TERM atom_enotconn;
static ERL_NIF_TERM atom_eoverflow;
+static ERL_NIF_TERM atom_eservice;
+static ERL_NIF_TERM atom_esocktype;
+static ERL_NIF_TERM atom_esystem;
// static ERL_NIF_TERM atom_exalloc;
// static ERL_NIF_TERM atom_exbadstate;
// static ERL_NIF_TERM atom_exbusy;
@@ -620,7 +667,7 @@ ERL_NIF_TERM nif_getnameinfo(ErlNifEnv* env,
unsigned int eFlags;
int flags = 0; // Just in case...
SockAddress sa;
- socklen_t saLen = 0; // Just in case...
+ SOCKLEN_T saLen = 0; // Just in case...
if ((argc != 2) ||
!GET_UINT(env, argv[1], &eFlags)) {
@@ -645,14 +692,14 @@ ERL_NIF_TERM nif_getnameinfo(ErlNifEnv* env,
static
ERL_NIF_TERM ngetnameinfo(ErlNifEnv* env,
const SockAddress* saP,
- socklen_t saLen,
+ SOCKLEN_T saLen,
int flags)
{
ERL_NIF_TERM result;
char host[HOSTNAME_LEN];
- socklen_t hostLen = sizeof(host);
+ SOCKLEN_T hostLen = sizeof(host);
char serv[SERVICE_LEN];
- socklen_t servLen = sizeof(serv);
+ SOCKLEN_T servLen = sizeof(serv);
int res = getnameinfo((struct sockaddr*) saP, saLen,
host, hostLen,
@@ -729,7 +776,7 @@ ERL_NIF_TERM nif_getaddrinfo(ErlNifEnv* env,
int argc,
const ERL_NIF_TERM argv[])
{
- ERL_NIF_TERM result;
+ ERL_NIF_TERM result, eHostName, eServName; //, eHints;
char* hostName;
char* servName;
// struct addrinfo* hints;
@@ -739,7 +786,7 @@ ERL_NIF_TERM nif_getaddrinfo(ErlNifEnv* env,
}
eHostName = argv[0];
eServName = argv[1];
- eHints = argv[2];
+ // eHints = argv[2];
if (!decode_addrinfo_string(env, eHostName, &hostName))
return enif_make_badarg(env);
@@ -773,10 +820,11 @@ ERL_NIF_TERM nif_getaddrinfo(ErlNifEnv* env,
static
-ERL_NIF_TERM nifgetaddrinfo(ErlNifEnv* env,
- char* host,
- char* serv)
+ERL_NIF_TERM ngetaddrinfo(ErlNifEnv* env,
+ char* host,
+ char* serv)
{
+ ERL_NIF_TERM result;
struct addrinfo* addrInfoP;
int res;
@@ -823,7 +871,7 @@ ERL_NIF_TERM nifgetaddrinfo(ErlNifEnv* env,
result = make_error(env, atom_enoname);
break;
- case EAI_SERCVICE:
+ case EAI_SERVICE:
result = make_error(env, atom_eservice);
break;
@@ -1041,7 +1089,7 @@ static
BOOLEAN_T decode_in_sockaddr(ErlNifEnv* env,
const ERL_NIF_TERM eAddr,
SockAddress* saP,
- socklen_t* saLen)
+ SOCKLEN_T* saLen)
{
const ERL_NIF_TERM* addrt;
int addrtSz;
@@ -1078,7 +1126,7 @@ static
BOOLEAN_T decode_in4_sockaddr(ErlNifEnv* env,
const ERL_NIF_TERM* addrt,
SockAddress* saP,
- socklen_t* saLen)
+ SOCKLEN_T* saLen)
{
unsigned int len;
char tag[16]; // Just in case...
@@ -1146,7 +1194,7 @@ static
BOOLEAN_T decode_in6_sockaddr(ErlNifEnv* env,
const ERL_NIF_TERM* addrt,
SockAddress* saP,
- socklen_t* saLen)
+ SOCKLEN_T* saLen)
{
unsigned int len;
char tag[16]; // Just in case...
@@ -1330,7 +1378,7 @@ BOOLEAN_T decode_addrinfo_string(ErlNifEnv* env,
result = FALSE;
}
- bufP = ALLOC(len);
+ bufP = MALLOC(len);
if (GET_STR(env, eString, bufP, len)) {
*stringP = bufP;
@@ -1366,7 +1414,7 @@ ERL_NIF_TERM encode_address_info(ErlNifEnv* env,
array[i] = make_address_info(env, &addrInfo[i]);
}
- result = mkake_ok2(env, MKLA(env, array, len));
+ result = make_ok2(env, MKLA(env, array, len));
} else {
result = MKEL(env);
}
@@ -1407,8 +1455,8 @@ unsigned int address_info_length(struct addrinfo* addrInfoP)
* {address_info, Fam, Type, Proto, Addr}
*/
static
-ERL_NIF_TERM make_adress_info(ErlNifEnv* env,
- struct addrinfo* addrInfoP)
+ERL_NIF_TERM make_address_info(ErlNifEnv* env,
+ struct addrinfo* addrInfoP)
{
ERL_NIF_TERM Fam = make_addrinfo_family(env, addrInfoP->ai_family);
ERL_NIF_TERM Type = make_addrinfo_type(env, addrInfoP->ai_socktype);
@@ -1530,35 +1578,41 @@ ERL_NIF_TERM make_addrinfo_proto(ErlNifEnv* env,
static
ERL_NIF_TERM make_addrinfo_addr(ErlNifEnv* env,
struct sockaddr* addrP,
- socklen_t addrLen)
+ SOCKLEN_T addrLen)
{
ERL_NIF_TERM port, addr, eaddr;
SockAddress* p = (SockAddress*) addrP;
switch (addrP->sa_family) {
case AF_INET:
- port = ntohs(p->in.sin_port);
- addr = MKT4(env,
- MKI(env, p->in.sin_addr[0]),
- MKI(env, p->in.sin_addr[1]),
- MKI(env, p->in.sin_addr[2]),
- MKI(env, p->in.sin_addr[3]));
- eaddr = MKT2(env, port, addr);
+ {
+ unsigned char* a = (unsigned char*) &p->in.sin_addr;
+ port = ntohs(p->in.sin_port);
+ addr = MKT4(env,
+ MKI(env, a[0]),
+ MKI(env, a[1]),
+ MKI(env, a[2]),
+ MKI(env, a[3]));
+ eaddr = MKT2(env, port, addr);
+ }
break;
#if defined(HAVE_IN6) && defined(AF_INET6)
case AF_INET6:
- port = ntohs(p->in6.sin6_port);
- addr = MKT8(env,
- MKI(env, p->in6.sin_addr[0]),
- MKI(env, p->in6.sin_addr[1]),
- MKI(env, p->in6.sin_addr[2]),
- MKI(env, p->in6.sin_addr[3]),
- MKI(env, p->in6.sin_addr[3]),
- MKI(env, p->in6.sin_addr[3]),
- MKI(env, p->in6.sin_addr[3]),
- MKI(env, p->in6.sin_addr[3]));
- eaddr = MKT2(env, port, addr);
+ {
+ unsigned char* a = (unsigned char*) &p->in6.sin6_addr;
+ port = ntohs(p->in6.sin6_port);
+ addr = MKT8(env,
+ MKI(env, get_int16(a)),
+ MKI(env, get_int16(&a[ 2])),
+ MKI(env, get_int16(&a[ 4])),
+ MKI(env, get_int16(&a[ 6])),
+ MKI(env, get_int16(&a[ 8])),
+ MKI(env, get_int16(&a[10])),
+ MKI(env, get_int16(&a[12])),
+ MKI(env, get_int16(&a[14])));
+ eaddr = MKT2(env, port, addr);
+ }
break;
#endif
@@ -1713,6 +1767,7 @@ ErlNifFunc net_funcs[] =
/* address and name translation in protocol-independent manner */
{"nif_getnameinfo", 2, nif_getnameinfo, 0},
+ {"nif_getaddrinfo", 3, nif_getaddrinfo, 0},
/* Network interface (name and/or index) functions */
{"nif_if_name2index", 1, nif_if_name2index, 0},
@@ -1730,18 +1785,25 @@ static
int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info)
{
/* +++ Misc atoms +++ */
+ atom_address_info = MKA(env, str_address_info);
atom_dgram = MKA(env, str_dgram);
atom_error = MKA(env, str_error);
atom_idn = MKA(env, str_idn);
atom_idna_allow_unassigned = MKA(env, str_idna_allow_unassigned);
atom_idna_use_std3_ascii_rules = MKA(env, str_idna_use_std3_ascii_rules);
+ atom_inet = MKA(env, str_inet);
+ atom_inet6 = MKA(env, str_inet6);
+ atom_ip = MKA(env, str_ip);
atom_namereqd = MKA(env, str_namereqd);
atom_name_info = MKA(env, str_name_info);
atom_nofqdn = MKA(env, str_nofqdn);
atom_numerichost = MKA(env, str_numerichost);
atom_numericserv = MKA(env, str_numericserv);
atom_ok = MKA(env, str_ok);
+ atom_stream = MKA(env, str_stream);
+ atom_tcp = MKA(env, str_tcp);
atom_true = MKA(env, str_true);
+ atom_udp = MKA(env, str_udp);
atom_undefined = MKA(env, str_undefined);
// atom_version = MKA(env, str_version);
@@ -1752,6 +1814,7 @@ int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info)
/* Error codes */
// atom_eafnosupport = MKA(env, str_eafnosupport);
+ atom_eaddrfamily = MKA(env, str_eaddrfamily);
atom_eagain = MKA(env, str_eagain);
atom_ebadflags = MKA(env, str_ebadflags);
atom_efail = MKA(env, str_efail);
@@ -1759,10 +1822,14 @@ int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info)
atom_einval = MKA(env, str_einval);
// atom_eisconn = MKA(env, str_eisconn);
atom_emem = MKA(env, str_emem);
+ atom_enodata = MKA(env, str_enodata);
atom_enoname = MKA(env, str_enoname);
// atom_enotclosing = MKA(env, str_enotclosing);
// atom_enotconn = MKA(env, str_enotconn);
atom_eoverflow = MKA(env, str_eoverflow);
+ atom_eservice = MKA(env, str_eservice);
+ atom_esocktype = MKA(env, str_esocktype);
+ atom_esystem = MKA(env, str_esystem);
// atom_exalloc = MKA(env, str_exalloc);
// atom_exbadstate = MKA(env, str_exbadstate);
// atom_exbusy = MKA(env, str_exbusy);
diff --git a/erts/preloaded/ebin/init.beam b/erts/preloaded/ebin/init.beam
index 6658ff1a33..9c8973845b 100644
--- a/erts/preloaded/ebin/init.beam
+++ b/erts/preloaded/ebin/init.beam
Binary files differ
diff --git a/erts/preloaded/ebin/net.beam b/erts/preloaded/ebin/net.beam
new file mode 100644
index 0000000000..a5746fb8c9
--- /dev/null
+++ b/erts/preloaded/ebin/net.beam
Binary files differ
diff --git a/erts/preloaded/src/init.erl b/erts/preloaded/src/init.erl
index 303301cbff..b02e5dce85 100644
--- a/erts/preloaded/src/init.erl
+++ b/erts/preloaded/src/init.erl
@@ -206,6 +206,7 @@ boot(BootArgs) ->
erl_tracer:on_load(),
prim_buffer:on_load(),
prim_file:on_load(),
+ net:on_load(),
{Start0,Flags,Args} = parse_boot_args(BootArgs),
%% We don't get to profile parsing of BootArgs
diff --git a/erts/preloaded/src/net.erl b/erts/preloaded/src/net.erl
index bdd81ea93a..f145797b97 100644
--- a/erts/preloaded/src/net.erl
+++ b/erts/preloaded/src/net.erl
@@ -131,7 +131,7 @@ on_load(Path, Extra) when is_list(Path) andalso is_map(Extra) ->
on_load(true, _Path, _Extra) ->
ok;
on_load(false, Path, Extra) ->
- ok = erlang:load_nif(Path, maps:put(timestamp, formated_timestamp(), Extra)).
+ ok = erlang:load_nif(Path, Extra).
@@ -174,7 +174,7 @@ getnameinfo(SockAddr, Flags)
when (is_record(SockAddr, in4_sockaddr) orelse
is_record(SockAddr, in6_sockaddr)) andalso
is_list(Flags) ->
- nif_getnameinfo(SockAddr, EFlags).
+ nif_getnameinfo(SockAddr, Flags).
%% ===========================================================================
@@ -254,30 +254,30 @@ if_names() ->
%%
%% ===========================================================================
-formated_timestamp() ->
- format_timestamp(os:timestamp()).
-
-format_timestamp(Now) ->
- N2T = fun(N) -> calendar:now_to_local_time(N) end,
- format_timestamp(Now, N2T, true).
-
-format_timestamp({_N1, _N2, N3} = N, N2T, true) ->
- FormatExtra = ".~.2.0w",
- ArgsExtra = [N3 div 10000],
- format_timestamp(N, N2T, FormatExtra, ArgsExtra);
-format_timestamp({_N1, _N2, _N3} = N, N2T, false) ->
- FormatExtra = "",
- ArgsExtra = [],
- format_timestamp(N, N2T, FormatExtra, ArgsExtra).
-
-format_timestamp(N, N2T, FormatExtra, ArgsExtra) ->
- {Date, Time} = N2T(N),
- {YYYY,MM,DD} = Date,
- {Hour,Min,Sec} = Time,
- FormatDate =
- io_lib:format("~.4w-~.2.0w-~.2.0w ~.2.0w:~.2.0w:~.2.0w" ++ FormatExtra,
- [YYYY, MM, DD, Hour, Min, Sec] ++ ArgsExtra),
- lists:flatten(FormatDate).
+%% formated_timestamp() ->
+%% format_timestamp(os:timestamp()).
+
+%% format_timestamp(Now) ->
+%% N2T = fun(N) -> calendar:now_to_local_time(N) end,
+%% format_timestamp(Now, N2T, true).
+
+%% format_timestamp({_N1, _N2, N3} = N, N2T, true) ->
+%% FormatExtra = ".~.2.0w",
+%% ArgsExtra = [N3 div 10000],
+%% format_timestamp(N, N2T, FormatExtra, ArgsExtra);
+%% format_timestamp({_N1, _N2, _N3} = N, N2T, false) ->
+%% FormatExtra = "",
+%% ArgsExtra = [],
+%% format_timestamp(N, N2T, FormatExtra, ArgsExtra).
+
+%% format_timestamp(N, N2T, FormatExtra, ArgsExtra) ->
+%% {Date, Time} = N2T(N),
+%% {YYYY,MM,DD} = Date,
+%% {Hour,Min,Sec} = Time,
+%% FormatDate =
+%% io_lib:format("~.4w-~.2.0w-~.2.0w ~.2.0w:~.2.0w:~.2.0w" ++ FormatExtra,
+%% [YYYY, MM, DD, Hour, Min, Sec] ++ ArgsExtra),
+%% lists:flatten(FormatDate).
%% ===========================================================================