提交 bd0f7fff 编写于 作者: S Shengliang Guan

Merge remote-tracking branch 'origin/develop' into feature/TD-1925

...@@ -122,17 +122,12 @@ void dnodeFreeMPeerQueue() { ...@@ -122,17 +122,12 @@ void dnodeFreeMPeerQueue() {
} }
void dnodeDispatchToMPeerQueue(SRpcMsg *pMsg) { void dnodeDispatchToMPeerQueue(SRpcMsg *pMsg) {
if (!mnodeIsRunning()) { if (!mnodeIsRunning() || tsMPeerQueue == NULL) {
dnodeSendRedirectMsg(pMsg, false); dnodeSendRedirectMsg(pMsg, false);
} else {
if (!mnodeIsReady()) {
SRpcMsg rpcRsp = {.handle = pMsg->handle, .code = mnodeInitCode(), .pCont = NULL};
rpcSendResponse(&rpcRsp);
} else { } else {
SMnodeMsg *pPeer = mnodeCreateMsg(pMsg); SMnodeMsg *pPeer = mnodeCreateMsg(pMsg);
taosWriteQitem(tsMPeerQueue, TAOS_QTYPE_RPC, pPeer); taosWriteQitem(tsMPeerQueue, TAOS_QTYPE_RPC, pPeer);
} }
}
rpcFreeCont(pMsg->pCont); rpcFreeCont(pMsg->pCont);
} }
......
...@@ -123,17 +123,12 @@ void dnodeFreeMReadQueue() { ...@@ -123,17 +123,12 @@ void dnodeFreeMReadQueue() {
} }
void dnodeDispatchToMReadQueue(SRpcMsg *pMsg) { void dnodeDispatchToMReadQueue(SRpcMsg *pMsg) {
if (!mnodeIsRunning()) { if (!mnodeIsRunning() || tsMReadQueue == NULL) {
dnodeSendRedirectMsg(pMsg, true); dnodeSendRedirectMsg(pMsg, true);
} else {
if (!mnodeIsReady()) {
SRpcMsg rpcRsp = {.handle = pMsg->handle, .code = mnodeInitCode(), .pCont = NULL};
rpcSendResponse(&rpcRsp);
} else { } else {
SMnodeMsg *pRead = mnodeCreateMsg(pMsg); SMnodeMsg *pRead = mnodeCreateMsg(pMsg);
taosWriteQitem(tsMReadQueue, TAOS_QTYPE_RPC, pRead); taosWriteQitem(tsMReadQueue, TAOS_QTYPE_RPC, pRead);
} }
}
rpcFreeCont(pMsg->pCont); rpcFreeCont(pMsg->pCont);
} }
......
...@@ -123,19 +123,14 @@ void dnodeFreeMWritequeue() { ...@@ -123,19 +123,14 @@ void dnodeFreeMWritequeue() {
} }
void dnodeDispatchToMWriteQueue(SRpcMsg *pMsg) { void dnodeDispatchToMWriteQueue(SRpcMsg *pMsg) {
if (!mnodeIsRunning()) { if (!mnodeIsRunning() || tsMWriteQueue == NULL) {
dnodeSendRedirectMsg(pMsg, true); dnodeSendRedirectMsg(pMsg, true);
} else {
if (!mnodeIsReady()) {
SRpcMsg rpcRsp = {.handle = pMsg->handle, .code = mnodeInitCode(), .pCont = NULL};
rpcSendResponse(&rpcRsp);
} else { } else {
SMnodeMsg *pWrite = mnodeCreateMsg(pMsg); SMnodeMsg *pWrite = mnodeCreateMsg(pMsg);
dDebug("msg:%p, app:%p type:%s is put into mwrite queue:%p", pWrite, pWrite->rpcMsg.ahandle, dDebug("msg:%p, app:%p type:%s is put into mwrite queue:%p", pWrite, pWrite->rpcMsg.ahandle,
taosMsg[pWrite->rpcMsg.msgType], tsMWriteQueue); taosMsg[pWrite->rpcMsg.msgType], tsMWriteQueue);
taosWriteQitem(tsMWriteQueue, TAOS_QTYPE_RPC, pWrite); taosWriteQitem(tsMWriteQueue, TAOS_QTYPE_RPC, pWrite);
} }
}
rpcFreeCont(pMsg->pCont); rpcFreeCont(pMsg->pCont);
} }
...@@ -192,7 +187,7 @@ static void *dnodeProcessMWriteQueue(void *param) { ...@@ -192,7 +187,7 @@ static void *dnodeProcessMWriteQueue(void *param) {
void dnodeReprocessMWriteMsg(void *pMsg) { void dnodeReprocessMWriteMsg(void *pMsg) {
SMnodeMsg *pWrite = pMsg; SMnodeMsg *pWrite = pMsg;
if (!mnodeIsRunning()) { if (!mnodeIsRunning() || tsMWriteQueue == NULL) {
dDebug("msg:%p, app:%p type:%s is redirected for mnode not running, retry times:%d", pWrite, pWrite->rpcMsg.ahandle, dDebug("msg:%p, app:%p type:%s is redirected for mnode not running, retry times:%d", pWrite, pWrite->rpcMsg.ahandle,
taosMsg[pWrite->rpcMsg.msgType], pWrite->retry); taosMsg[pWrite->rpcMsg.msgType], pWrite->retry);
...@@ -201,6 +196,7 @@ void dnodeReprocessMWriteMsg(void *pMsg) { ...@@ -201,6 +196,7 @@ void dnodeReprocessMWriteMsg(void *pMsg) {
} else { } else {
dDebug("msg:%p, app:%p type:%s is reput into mwrite queue:%p, retry times:%d", pWrite, pWrite->rpcMsg.ahandle, dDebug("msg:%p, app:%p type:%s is reput into mwrite queue:%p, retry times:%d", pWrite, pWrite->rpcMsg.ahandle,
taosMsg[pWrite->rpcMsg.msgType], tsMWriteQueue, pWrite->retry); taosMsg[pWrite->rpcMsg.msgType], tsMWriteQueue, pWrite->retry);
taosWriteQitem(tsMWriteQueue, TAOS_QTYPE_RPC, pWrite); taosWriteQitem(tsMWriteQueue, TAOS_QTYPE_RPC, pWrite);
} }
} }
......
...@@ -64,7 +64,6 @@ static const SDnodeComponent tsDnodeComponents[] = { ...@@ -64,7 +64,6 @@ static const SDnodeComponent tsDnodeComponents[] = {
{"dnodeeps", dnodeInitEps, dnodeCleanupEps}, {"dnodeeps", dnodeInitEps, dnodeCleanupEps},
{"globalcfg" ,taosCheckGlobalCfg, NULL}, {"globalcfg" ,taosCheckGlobalCfg, NULL},
{"mnodeinfos",dnodeInitMInfos, dnodeCleanupMInfos}, {"mnodeinfos",dnodeInitMInfos, dnodeCleanupMInfos},
{"shell", dnodeInitShell, dnodeCleanupShell},
{"wal", walInit, walCleanUp}, {"wal", walInit, walCleanUp},
{"check", dnodeInitCheck, dnodeCleanupCheck}, // NOTES: dnodeInitCheck must be behind the dnodeinitStorage component !!! {"check", dnodeInitCheck, dnodeCleanupCheck}, // NOTES: dnodeInitCheck must be behind the dnodeinitStorage component !!!
{"vread", dnodeInitVRead, dnodeCleanupVRead}, {"vread", dnodeInitVRead, dnodeCleanupVRead},
...@@ -77,6 +76,7 @@ static const SDnodeComponent tsDnodeComponents[] = { ...@@ -77,6 +76,7 @@ static const SDnodeComponent tsDnodeComponents[] = {
{"mgmt", dnodeInitMgmt, dnodeCleanupMgmt}, {"mgmt", dnodeInitMgmt, dnodeCleanupMgmt},
{"modules", dnodeInitModules, dnodeCleanupModules}, {"modules", dnodeInitModules, dnodeCleanupModules},
{"mgmt-tmr", dnodeInitMgmtTimer, dnodeCleanupMgmtTimer}, {"mgmt-tmr", dnodeInitMgmtTimer, dnodeCleanupMgmtTimer},
{"shell", dnodeInitShell, dnodeCleanupShell},
{"telemetry", dnodeInitTelemetry, dnodeCleanupTelemetry}, {"telemetry", dnodeInitTelemetry, dnodeCleanupTelemetry},
}; };
......
...@@ -144,7 +144,7 @@ static void dnodeProcessMsgFromShell(SRpcMsg *pMsg, SRpcEpSet *pEpSet) { ...@@ -144,7 +144,7 @@ static void dnodeProcessMsgFromShell(SRpcMsg *pMsg, SRpcEpSet *pEpSet) {
static int dnodeRetrieveUserAuthInfo(char *user, char *spi, char *encrypt, char *secret, char *ckey) { static int dnodeRetrieveUserAuthInfo(char *user, char *spi, char *encrypt, char *secret, char *ckey) {
int code = mnodeRetriveAuth(user, spi, encrypt, secret, ckey); int code = mnodeRetriveAuth(user, spi, encrypt, secret, ckey);
if (code != TSDB_CODE_RPC_REDIRECT) return code; if (code != TSDB_CODE_APP_NOT_READY) return code;
SAuthMsg *pMsg = rpcMallocCont(sizeof(SAuthMsg)); SAuthMsg *pMsg = rpcMallocCont(sizeof(SAuthMsg));
tstrncpy(pMsg->user, user, sizeof(pMsg->user)); tstrncpy(pMsg->user, user, sizeof(pMsg->user));
......
...@@ -65,15 +65,12 @@ void mnodeStopSystem(); ...@@ -65,15 +65,12 @@ void mnodeStopSystem();
void sdbUpdateAsync(); void sdbUpdateAsync();
void sdbUpdateSync(void *pMnodes); void sdbUpdateSync(void *pMnodes);
bool mnodeIsRunning(); bool mnodeIsRunning();
bool mnodeIsReady();
int32_t mnodeInitCode();
int32_t mnodeProcessRead(SMnodeMsg *pMsg); int32_t mnodeProcessRead(SMnodeMsg *pMsg);
int32_t mnodeProcessWrite(SMnodeMsg *pMsg); int32_t mnodeProcessWrite(SMnodeMsg *pMsg);
int32_t mnodeProcessPeerReq(SMnodeMsg *pMsg); int32_t mnodeProcessPeerReq(SMnodeMsg *pMsg);
void mnodeProcessPeerRsp(SRpcMsg *pMsg); void mnodeProcessPeerRsp(SRpcMsg *pMsg);
int32_t mnodeRetriveAuth(char *user, char *spi, char *encrypt, char *secret, char *ckey); int32_t mnodeRetriveAuth(char *user, char *spi, char *encrypt, char *secret, char *ckey);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif
......
...@@ -125,9 +125,6 @@ TAOS_DEFINE_ERROR(TSDB_CODE_MND_INVALID_SHOWOBJ, 0, 0x030B, "Data expir ...@@ -125,9 +125,6 @@ TAOS_DEFINE_ERROR(TSDB_CODE_MND_INVALID_SHOWOBJ, 0, 0x030B, "Data expir
TAOS_DEFINE_ERROR(TSDB_CODE_MND_INVALID_QUERY_ID, 0, 0x030C, "Invalid query id") TAOS_DEFINE_ERROR(TSDB_CODE_MND_INVALID_QUERY_ID, 0, 0x030C, "Invalid query id")
TAOS_DEFINE_ERROR(TSDB_CODE_MND_INVALID_STREAM_ID, 0, 0x030D, "Invalid stream id") TAOS_DEFINE_ERROR(TSDB_CODE_MND_INVALID_STREAM_ID, 0, 0x030D, "Invalid stream id")
TAOS_DEFINE_ERROR(TSDB_CODE_MND_INVALID_CONN_ID, 0, 0x030E, "Invalid connection id") TAOS_DEFINE_ERROR(TSDB_CODE_MND_INVALID_CONN_ID, 0, 0x030E, "Invalid connection id")
TAOS_DEFINE_ERROR(TSDB_CODE_MND_INIT, 0, 0x030F, "Mnode is initializing")
TAOS_DEFINE_ERROR(TSDB_CODE_MND_INIT_SDB, 0, 0x0310, "Mnode is initializing meta data, it takes a while if many tables exists")
TAOS_DEFINE_ERROR(TSDB_CODE_MND_INIT_OTHER, 0, 0x0311, "Mnode is initializing other data")
TAOS_DEFINE_ERROR(TSDB_CODE_MND_SDB_OBJ_ALREADY_THERE, 0, 0x0320, "Object already there") TAOS_DEFINE_ERROR(TSDB_CODE_MND_SDB_OBJ_ALREADY_THERE, 0, 0x0320, "Object already there")
TAOS_DEFINE_ERROR(TSDB_CODE_MND_SDB_ERROR, 0, 0x0321, "Unexpected generic error in sdb") TAOS_DEFINE_ERROR(TSDB_CODE_MND_SDB_ERROR, 0, 0x0321, "Unexpected generic error in sdb")
......
...@@ -17,7 +17,6 @@ ...@@ -17,7 +17,6 @@
#include "os.h" #include "os.h"
#include "taosdef.h" #include "taosdef.h"
#include "tsched.h" #include "tsched.h"
#include "taoserror.h"
#include "tbalance.h" #include "tbalance.h"
#include "tgrant.h" #include "tgrant.h"
#include "ttimer.h" #include "ttimer.h"
...@@ -38,40 +37,30 @@ ...@@ -38,40 +37,30 @@
#include "mnodeShow.h" #include "mnodeShow.h"
#include "mnodeProfile.h" #include "mnodeProfile.h"
typedef enum {
TSDB_MND_STATUS_NOT_RUNNING,
TSDB_MND_STATUS_INIT,
TSDB_MND_STATUS_INIT_SDB,
TSDB_MND_STATUS_INIT_OTHER,
TSDB_MND_STATUS_READY,
TSDB_MND_STATUS_CLEANING,
} EMndStatus;
typedef struct { typedef struct {
const char *const name; const char *const name;
int (*init)(); int (*init)();
void (*cleanup)(); void (*cleanup)();
EMndStatus status;
} SMnodeComponent; } SMnodeComponent;
void *tsMnodeTmr = NULL; void *tsMnodeTmr = NULL;
static EMndStatus tsMgmtStatus = TSDB_MND_STATUS_NOT_RUNNING; static bool tsMgmtIsRunning = false;
static const SMnodeComponent tsMnodeComponents[] = { static const SMnodeComponent tsMnodeComponents[] = {
{"sdbref", sdbInitRef, sdbCleanUpRef, TSDB_MND_STATUS_INIT}, {"sdbref", sdbInitRef, sdbCleanUpRef},
{"profile", mnodeInitProfile, mnodeCleanupProfile, TSDB_MND_STATUS_INIT}, {"profile", mnodeInitProfile, mnodeCleanupProfile},
{"cluster", mnodeInitCluster, mnodeCleanupCluster, TSDB_MND_STATUS_INIT}, {"cluster", mnodeInitCluster, mnodeCleanupCluster},
{"accts", mnodeInitAccts, mnodeCleanupAccts, TSDB_MND_STATUS_INIT}, {"accts", mnodeInitAccts, mnodeCleanupAccts},
{"users", mnodeInitUsers, mnodeCleanupUsers, TSDB_MND_STATUS_INIT}, {"users", mnodeInitUsers, mnodeCleanupUsers},
{"dnodes", mnodeInitDnodes, mnodeCleanupDnodes, TSDB_MND_STATUS_INIT}, {"dnodes", mnodeInitDnodes, mnodeCleanupDnodes},
{"dbs", mnodeInitDbs, mnodeCleanupDbs, TSDB_MND_STATUS_INIT}, {"dbs", mnodeInitDbs, mnodeCleanupDbs},
{"vgroups", mnodeInitVgroups, mnodeCleanupVgroups, TSDB_MND_STATUS_INIT}, {"vgroups", mnodeInitVgroups, mnodeCleanupVgroups},
{"tables", mnodeInitTables, mnodeCleanupTables, TSDB_MND_STATUS_INIT}, {"tables", mnodeInitTables, mnodeCleanupTables},
{"mnodes", mnodeInitMnodes, mnodeCleanupMnodes, TSDB_MND_STATUS_INIT}, {"mnodes", mnodeInitMnodes, mnodeCleanupMnodes},
{"sdb", sdbInit, sdbCleanUp, TSDB_MND_STATUS_INIT_SDB}, {"sdb", sdbInit, sdbCleanUp},
{"balance", balanceInit, balanceCleanUp, TSDB_MND_STATUS_INIT_OTHER}, {"balance", balanceInit, balanceCleanUp},
{"grant", grantInit, grantCleanUp, TSDB_MND_STATUS_INIT_OTHER}, {"grant", grantInit, grantCleanUp},
{"show", mnodeInitShow, mnodeCleanUpShow, TSDB_MND_STATUS_INIT_OTHER}, {"show", mnodeInitShow, mnodeCleanUpShow}
}; };
static void mnodeInitTimer(); static void mnodeInitTimer();
...@@ -87,24 +76,21 @@ static void mnodeCleanupComponents(int32_t stepId) { ...@@ -87,24 +76,21 @@ static void mnodeCleanupComponents(int32_t stepId) {
static int32_t mnodeInitComponents() { static int32_t mnodeInitComponents() {
int32_t code = 0; int32_t code = 0;
for (int32_t i = 0; i < sizeof(tsMnodeComponents) / sizeof(tsMnodeComponents[0]); i++) { for (int32_t i = 0; i < sizeof(tsMnodeComponents) / sizeof(tsMnodeComponents[0]); i++) {
tsMgmtStatus = tsMnodeComponents[i].status;
if (tsMnodeComponents[i].init() != 0) { if (tsMnodeComponents[i].init() != 0) {
mnodeCleanupComponents(i); mnodeCleanupComponents(i);
code = -1; code = -1;
break; break;
} }
// sleep(3);
} }
return code; return code;
} }
int32_t mnodeStartSystem() { int32_t mnodeStartSystem() {
if (tsMgmtStatus != TSDB_MND_STATUS_NOT_RUNNING) { if (tsMgmtIsRunning) {
mInfo("mnode module already started..."); mInfo("mnode module already started...");
return 0; return 0;
} }
tsMgmtStatus = TSDB_MND_STATUS_INIT;
mInfo("starting to initialize mnode ..."); mInfo("starting to initialize mnode ...");
if (mkdir(tsMnodeDir, 0755) != 0 && errno != EEXIST) { if (mkdir(tsMnodeDir, 0755) != 0 && errno != EEXIST) {
mError("failed to init mnode dir:%s, reason:%s", tsMnodeDir, strerror(errno)); mError("failed to init mnode dir:%s, reason:%s", tsMnodeDir, strerror(errno));
...@@ -120,7 +106,7 @@ int32_t mnodeStartSystem() { ...@@ -120,7 +106,7 @@ int32_t mnodeStartSystem() {
} }
grantReset(TSDB_GRANT_ALL, 0); grantReset(TSDB_GRANT_ALL, 0);
tsMgmtStatus = TSDB_MND_STATUS_READY; tsMgmtIsRunning = true;
mInfo("mnode is initialized successfully"); mInfo("mnode is initialized successfully");
...@@ -138,9 +124,9 @@ int32_t mnodeInitSystem() { ...@@ -138,9 +124,9 @@ int32_t mnodeInitSystem() {
} }
void mnodeCleanupSystem() { void mnodeCleanupSystem() {
if (mnodeIsRunning()) { if (tsMgmtIsRunning) {
mInfo("starting to clean up mnode"); mInfo("starting to clean up mnode");
tsMgmtStatus = TSDB_MND_STATUS_CLEANING; tsMgmtIsRunning = false;
dnodeFreeMWritequeue(); dnodeFreeMWritequeue();
dnodeFreeMReadQueue(); dnodeFreeMReadQueue();
...@@ -148,7 +134,6 @@ void mnodeCleanupSystem() { ...@@ -148,7 +134,6 @@ void mnodeCleanupSystem() {
mnodeCleanupTimer(); mnodeCleanupTimer();
mnodeCleanupComponents(sizeof(tsMnodeComponents) / sizeof(tsMnodeComponents[0]) - 1); mnodeCleanupComponents(sizeof(tsMnodeComponents) / sizeof(tsMnodeComponents[0]) - 1);
tsMgmtStatus = TSDB_MND_STATUS_NOT_RUNNING;
mInfo("mnode is cleaned up"); mInfo("mnode is cleaned up");
} }
} }
...@@ -199,29 +184,5 @@ static bool mnodeNeedStart() { ...@@ -199,29 +184,5 @@ static bool mnodeNeedStart() {
} }
bool mnodeIsRunning() { bool mnodeIsRunning() {
return (tsMgmtStatus != TSDB_MND_STATUS_NOT_RUNNING && tsMgmtStatus != TSDB_MND_STATUS_CLEANING); return tsMgmtIsRunning;
}
bool mnodeIsReady() {
return (tsMgmtStatus == TSDB_MND_STATUS_READY);
}
int32_t mnodeInitCode() {
int32_t code = -1;
switch (tsMgmtStatus) {
case TSDB_MND_STATUS_INIT:
code = TSDB_CODE_MND_INIT;
break;
case TSDB_MND_STATUS_INIT_SDB:
code = TSDB_CODE_MND_INIT_SDB;
break;
case TSDB_MND_STATUS_INIT_OTHER:
code = TSDB_CODE_MND_INIT_OTHER;
break;
default:
code = TSDB_CODE_MND_INIT;
}
return code;
} }
...@@ -309,7 +309,7 @@ void sdbUpdateAsync() { ...@@ -309,7 +309,7 @@ void sdbUpdateAsync() {
void sdbUpdateSync(void *pMnodes) { void sdbUpdateSync(void *pMnodes) {
SMnodeInfos *mnodes = pMnodes; SMnodeInfos *mnodes = pMnodes;
if (!mnodeIsReady()) { if (!mnodeIsRunning()) {
mDebug("vgId:1, mnode not start yet, update sync config later"); mDebug("vgId:1, mnode not start yet, update sync config later");
return; return;
} }
......
...@@ -585,21 +585,10 @@ void mnodeDropAllUsers(SAcctObj *pAcct) { ...@@ -585,21 +585,10 @@ void mnodeDropAllUsers(SAcctObj *pAcct) {
} }
int32_t mnodeRetriveAuth(char *user, char *spi, char *encrypt, char *secret, char *ckey) { int32_t mnodeRetriveAuth(char *user, char *spi, char *encrypt, char *secret, char *ckey) {
*secret = 0;
if (!mnodeIsRunning()) {
mDebug("user:%s, mnode is not running, fail to auth", user);
return TSDB_CODE_RPC_REDIRECT;
}
if (!mnodeIsReady()) {
mDebug("user:%s, failed to auth user, mnode is not ready", user);
return mnodeInitCode();
}
if (!sdbIsMaster()) { if (!sdbIsMaster()) {
*secret = 0;
mDebug("user:%s, failed to auth user, mnode is not master", user); mDebug("user:%s, failed to auth user, mnode is not master", user);
return TSDB_CODE_RPC_REDIRECT; return TSDB_CODE_APP_NOT_READY;
} }
SUserObj *pUser = mnodeGetUser(user); SUserObj *pUser = mnodeGetUser(user);
......
...@@ -1559,14 +1559,10 @@ static int rpcCheckAuthentication(SRpcConn *pConn, char *msg, int msgLen) { ...@@ -1559,14 +1559,10 @@ static int rpcCheckAuthentication(SRpcConn *pConn, char *msg, int msgLen) {
if ( !rpcIsReq(pHead->msgType) ) { if ( !rpcIsReq(pHead->msgType) ) {
// for response, if code is auth failure, it shall bypass the auth process // for response, if code is auth failure, it shall bypass the auth process
code = htonl(pHead->code); code = htonl(pHead->code);
#if 0
if (code != 0) {
#else
if (code == TSDB_CODE_RPC_INVALID_TIME_STAMP || code == TSDB_CODE_RPC_AUTH_FAILURE || if (code == TSDB_CODE_RPC_INVALID_TIME_STAMP || code == TSDB_CODE_RPC_AUTH_FAILURE ||
code == TSDB_CODE_RPC_AUTH_REQUIRED || code == TSDB_CODE_MND_INVALID_USER || code == TSDB_CODE_RPC_NOT_READY) { code == TSDB_CODE_RPC_AUTH_REQUIRED || code == TSDB_CODE_MND_INVALID_USER || code == TSDB_CODE_RPC_NOT_READY) {
#endif
// tTrace("%s, dont check authentication since code is:0x%x", pConn->info, code);
pHead->msgLen = (int32_t)htonl((uint32_t)pHead->msgLen); pHead->msgLen = (int32_t)htonl((uint32_t)pHead->msgLen);
// tTrace("%s, dont check authentication since code is:0x%x", pConn->info, code);
return 0; return 0;
} }
} }
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册