123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596 |
- #include "StdAfx.h"
- #include "SATDevices.h"
- #include "SATExecutor.h"
- bool CSATDevices::s_bEnableAndroid = true;
- ULONGLONG CSATDevices::s_ulEraseDuration = 30000;
- ThreadSection CSATDevices::s_ThreadSection;
- std::vector<SATDEV::STDevice> CSATDevices::s_vtDevices;
- bool CSATDevices::s_bAutoAddAdbThreadFinished = true;
- void Split(vector<std::string>& vtLine, std::string strLines, const std::string strSplit)
- {
- if (strLines.size() == 0 || strSplit.size() == 0)
- return;
- int nIndex = 0;
- vtLine.clear();
- std::string strtmp;
- while (std::string::npos != (nIndex = strLines.find(strSplit))) {
- strtmp = strLines.substr(0, nIndex);
- if (strtmp.size())vtLine.push_back(strtmp);
- strLines = strLines.substr(nIndex + strSplit.size());
- }
- if (strLines.size())
- vtLine.push_back(strLines);
- }
- std::string ExecuteCMD(std::string cmd)
- {
- SECURITY_ATTRIBUTES sa = {0};
- HANDLE hRead = NULL, hWrite = NULL;
- sa.nLength = sizeof(SECURITY_ATTRIBUTES);
- sa.lpSecurityDescriptor = NULL;
- sa.bInheritHandle = TRUE;
- // 创建管道;
- if ( !CreatePipe(&hRead, &hWrite, &sa, 1024) )
- {
- return "";
- }
- STARTUPINFO si = {0};
- PROCESS_INFORMATION pi = {0};
- si.cb = sizeof(STARTUPINFO);
- GetStartupInfo(&si);
- si.hStdError = hWrite;
- si.hStdOutput = hWrite;
- si.wShowWindow = SW_HIDE;
- si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
- // 创建子进程;
- TCHAR szCommand[MAX_PATH] = {0};
- _tcscpy_s(szCommand,cmd.c_str());
- if ( !CreateProcess(NULL, szCommand, NULL, NULL, TRUE, NULL, NULL, NULL, &si, &pi) )
- {
- CloseHandle(hRead);
- CloseHandle(hWrite);
- hWrite = hRead = NULL;
- }
- // 等待进程结束;
- WaitForSingleObject(pi.hProcess, INFINITE);
- // 释放所有句柄;
- CloseHandle(pi.hProcess);
- CloseHandle(pi.hThread);
- if ( hWrite ) {
- CloseHandle(hWrite);
- hWrite = NULL;
- }
- // 等待缓冲写入;
- Sleep(200);
- // 读取内容;
- DWORD dwBytesRead;
- TCHAR szBuffer[1024] = {0};
- ReadFile(hRead, szBuffer, 1024, &dwBytesRead, NULL);
- CloseHandle(hRead);
- // 返回结果;
- return std::string(szBuffer);
- }
- CSATDevices::CSATDevices(void)
- {
- m_hEvent = NULL;
- m_hWorkThread = NULL;
- // 添加一个虚拟设备;
- AddVirtualDevices("192.168.1.999:5555");
- }
- CSATDevices::~CSATDevices(void)
- {
- EndofWork();
- }
- void CSATDevices::StartWork()
- {
- m_hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
- if ( m_hEvent == NULL ) {
- return;
- }
- m_hWorkThread = CreateThread(NULL, 0, WorkThread, this, 0, NULL);
- if ( m_hWorkThread == NULL) {
- SetEvent(m_hEvent);
- CloseHandle(m_hEvent);
- CloseHandle(m_hWorkThread);
- m_hEvent = NULL;
- m_hWorkThread = NULL;
- return;
- }
- }
- void CSATDevices::EndofWork()
- {
- // 设置事件有信号;
- if ( m_hEvent )
- SetEvent(m_hEvent);
- // 等待线程结束;
- if ( m_hWorkThread ) {
- WaitForSingleObject(m_hWorkThread, INFINITE);
- CloseHandle(m_hWorkThread);
- m_hWorkThread = NULL;
- }
- // 关闭事件句柄;
- if ( m_hEvent ) {
- CloseHandle(m_hEvent);
- m_hEvent = NULL;
- }
- }
- DWORD CSATDevices::WorkThread(LPVOID lpVoid)
- {
- CSATDevices *that = (CSATDevices*)lpVoid;
- do
- {
- if ( !s_bEnableAndroid )
- continue;
- // 刷新当前设备列表;
- std::vector<SATDEV::STDevice> vtDevices;
- // 获取当前设备列表;
- GetCurrentDevices(vtDevices);
- AutoThreadSection ats(&s_ThreadSection);
- #ifdef _DEBUG
- //TRACE(_T("\t=================start==================\n"));
- #endif
- // 遍历设备列表是否有下线设备;
- std::vector<SATDEV::STDevice>::iterator it = s_vtDevices.begin();
- for ( ;it != s_vtDevices.end(); ) {
- #ifdef _DEBUG
- //TRACE3(_T("\t当前设备类型:%d, 设备名称:%s, 设备状态:%d\n"), it->nType, it->strName.c_str(), it->nStatus);
- #endif
- if ( it->nType == SATDEV::Virtual ) {
- it++;
- continue;
- }
- if ( IsDevicesOffline(*it, vtDevices) ) {
- #ifdef _DEBUG
- printf("IsDevicesOffline\n");
- #endif
- if ( (GetTickCount64() - it->ulOfflineTime) > GLOBAL::g_stSATConfig.dwAdbTimeout ) {
- GLOBAL::WriteTextLog(GLOBAL::SAT_DEV, "移除设备->设备类型:%d, 设备名称:%s, 状态:%d", it->nType, it->strName.c_str(), it->nStatus);
- // 移除设备(需要线程加锁);
- it = s_vtDevices.erase(it);
- } else {
- if ( it->nType == SATDEV::Reticle ) {
- if ( it->nStatus == SATDEV::Offline ) {
- // offline只能reconnect才能重连;
- std::string str = "adb reconnect offline";
- WinExec(str.c_str(), SW_HIDE);
- GLOBAL::WriteTextLog(GLOBAL::SAT_DEV, "offline重连->设备类型:%d, 设备名称:%s, 状态:%d, 命令:%s", it->nType, it->strName.c_str(), it->nStatus, str.c_str());
- }
- else if ( it->nStatus == SATDEV::Dropline ) {
- std::string str = "adb connect ";
- str.append(it->strName);
- WinExec(str.c_str(), SW_HIDE);
- GLOBAL::WriteTextLog(GLOBAL::SAT_DEV, "dropline重连->设备类型:%d, 设备名称:%s, 状态:%d, 命令:%s", it->nType, it->strName.c_str(), it->nStatus, str.c_str());
- }
- // 等待adb重连完成;
- Sleep(2500);
- } else if ( it->nType == SATDEV::Usb ) {
- #if 1
- if ( it->nStatus == SATDEV::Offline ) {
- // offline需要reconnect才能重连;
- std::string str = "adb reconnect offline";
- WinExec(str.c_str(), SW_HIDE);
- GLOBAL::WriteTextLog(GLOBAL::SAT_DEV, "usb重连->设备类型:%d, 设备名称:%s, 状态:%d, 命令:%s", it->nType, it->strName.c_str(), it->nStatus, str.c_str());
- // 等待adb启动;
- Sleep(2500);
- }
-
- // 不管状态是Offline还是Dropline,只要重连超过指定时长, 执行kill;
- if ( it->ulOfflineTime != 0 && (GetTickCount64() - it->ulOfflineTime > GLOBAL::g_stSATConfig.dwAdbKillTime) ) { // 1/2掉线时长太久,固定9秒时间,大概3次重连;
- std::string str = "adb kill-server";
- // 如果是usb的话,可能要kill-server,再全部重连接;
- WinExec(str.c_str(), SW_HIDE);
- // 重连所有;
- ReConnectAllDevices();
- // 重连后,break此次循环;
- GLOBAL::WriteTextLog(GLOBAL::SAT_DEV, "usb重连->设备类型:%d, 设备名称:%s, 状态:%d, 命令:%s", it->nType, it->strName.c_str(), it->nStatus, str.c_str());
- // 等待adb启动;
- Sleep(3500);
- // kill后,需要重新GetCurrentDevices查询;
- break;
- }
- #else
- std::string str = "adb kill-server";
- // 如果是usb的话,可能要kill-server,再全部重连接;
- WinExec(str.c_str(), SW_HIDE);
- // 重连所有;
- ReConnectAllDevices();
- // 重连后,break此次循环;
- GLOBAL::WriteTextLog("usb重连->设备类型:%d, 设备名称:%s, 状态:%d, 命令:%s", it->nType, it->strName.c_str(), it->nStatus, str.c_str());
- // 等待adb启动;
- Sleep(3500);
- // kill后,需要重新GetCurrentDevices查询;
- break;
- #endif
- }
- it++;
- }
- }
- else
- it++;
- }
- // 保存全部设备状态;
- SaveAllDevicesStatus2Config();
- #ifdef _DEBUG
- //TRACE(_T("\t=================end==================\n"));
- #endif
- } while (WaitForSingleObject(that->m_hEvent, 2500) == WAIT_TIMEOUT);
- printf("end thread\n");
- return 0;
- }
- void CSATDevices::DelDevices(std::string name)
- {
- std::string str;
- AutoThreadSection ats(&s_ThreadSection);
- std::vector<SATDEV::STDevice>::iterator it = s_vtDevices.begin();
- for(; it != s_vtDevices.end(); it++ ) {
- if ( _tcscmp(it->strName.c_str(), name.c_str()) == 0 ) {
- // usb设备没办法删除,此处忽略;
- if ( it->nType != SATDEV::Usb) {
- // 只有网络设备才能删除;
- str = "adb disconnect ";
- str.append(name);
- WinExec(str.c_str(), SW_HIDE);
- CSATExecutor::GetInstance()->DelDevices(*it);
- s_vtDevices.erase(it);
- #ifdef _DEBUG
- TRACE(_T("\t删除设备:%s\n"), name.c_str());
- #endif
- }
- break;
- }
- }
- }
- void CSATDevices::AddReticleDevices(std::string ip)
- {
- // 处理自动添加adb;
- if ( ip.compare("0.0.0.0") == 0 ) {
- if (s_bAutoAddAdbThreadFinished) {
- // 启动adb设备自动添加线程;
- HANDLE h = CreateThread(NULL, 0, AddAdbThread, NULL, 0, NULL);
- CloseHandle(h);
- }
- return;
- }
- SATDEV::STDevice stDevice;
- stDevice.nType = SATDEV::Reticle;
- stDevice.strName = ip;
- stDevice.ulOfflineTime = 0;
- stDevice.nStatus = SATDEV::Online;
- stDevice.nUsageState = SATDEV::Idle;
- #ifdef _DEBUG
- TRACE1(_T("\t添加新设备:%s\n"), ip.c_str());
- #endif
- AutoThreadSection ats(&s_ThreadSection);
- if (!IsDeviceExist(stDevice)) {
- s_vtDevices.push_back(stDevice);
- CSATExecutor::GetInstance()->AddDevices(stDevice);
- }
- }
- void CSATDevices::AddVirtualDevices(std::string name)
- {
- SATDEV::STDevice stDevice;
- stDevice.nType = SATDEV::Virtual;
- stDevice.strName = name;
- stDevice.ulOfflineTime = 0;
- stDevice.nStatus = SATDEV::Online;
- stDevice.nUsageState = SATDEV::Idle;
- AutoThreadSection ats(&s_ThreadSection);
- if (!IsDeviceExist(stDevice)) {
- s_vtDevices.push_back(stDevice);
- CSATExecutor::GetInstance()->AddDevices(stDevice);
- }
- }
- bool CSATDevices::IsDeviceExist(SATDEV::STDevice &stDevice)
- {
- bool bExist = false;
- std::vector<SATDEV::STDevice>::iterator it = s_vtDevices.begin();
- for(; it != s_vtDevices.end(); it++ ) {
- if ( _tcscmp(it->strName.c_str(), stDevice.strName.c_str()) == 0 ) {
- bExist = true;
- break;
- }
- }
- return bExist;
- }
- bool CSATDevices::IsDevicesOffline(SATDEV::STDevice &stDevice, std::vector<SATDEV::STDevice> &vtDevices )
- {
- bool bFound = false;
- bool bOffline = true;
- // 在当前设备列表查询;
- std::vector<SATDEV::STDevice>::iterator it = vtDevices.begin();
- for(; it != vtDevices.end(); it++ ) {
- if ( _tcscmp(it->strName.c_str(), stDevice.strName.c_str()) == 0 ) {
- // 找到设备;
- bFound = true;
- // 同步状态;
- stDevice.nStatus = it->nStatus;
- // 设备是否offline或dropline;
- if ( it->nStatus != SATDEV::Offline && it->nStatus != SATDEV::Dropline)
- bOffline = false;
- break;
- }
- }
- if ( bOffline ) {
- if ( stDevice.ulOfflineTime == 0 ) {
- // 首次检测到离线;
- if ( stDevice.nStatus != SATDEV::Offline )
- stDevice.nStatus = SATDEV::Dropline;
- stDevice.ulOfflineTime = GetTickCount64();
- }
- // 如果没找到设备,彻底离线;
- if ( !bFound )
- stDevice.nStatus = SATDEV::Dropline;
- GLOBAL::WriteTextLog(GLOBAL::SAT_DEV, "离线->设备类型:%d, 设备名称:%s, 状态:%d", stDevice.nType, stDevice.strName.c_str(), stDevice.nStatus);
- } else {
- // 若重新连接,重置离线时间;
- if ( stDevice.ulOfflineTime ) {
- stDevice.nStatus = SATDEV::Online;
- stDevice.ulOfflineTime = 0;
- GLOBAL::WriteTextLog(GLOBAL::SAT_DEV, "重连成功->设备类型:%d, 设备名称:%s, 状态:%d", stDevice.nType, stDevice.strName.c_str(), stDevice.nStatus);
- }
- }
- return bOffline;
- }
- bool CSATDevices::IsNewDevices(SATDEV::STDevice &stDevice)
- {
- bool bNewDevices = true;
- std::vector<SATDEV::STDevice>::iterator it = s_vtDevices.begin();
- for(; it != s_vtDevices.end(); it++ ) {
- if ( _tcscmp(it->strName.c_str(), stDevice.strName.c_str()) == 0 ) {
- bNewDevices = false;
- break;
- }
- }
- // 如果是新设备(一般是usb连接)
- if ( bNewDevices && stDevice.nType != SATDEV::Reticle ) {
- AutoThreadSection ats(&s_ThreadSection);
- s_vtDevices.push_back(stDevice);
- CSATExecutor::GetInstance()->AddDevices(stDevice);
- }
- return bNewDevices;
- }
- void CSATDevices::GetCurrentDevices(std::vector<SATDEV::STDevice> &vtDevices)
- {
- std::string strLines = ExecuteCMD("adb devices");
- std::vector<std::string> vtLine;
- Split(vtLine, strLines, "\r\n");
- int npos = -1;
- // offline设备也要加入,不区分usb或reticle;
- for ( std::vector<std::string>::iterator it = vtLine.begin(); it != vtLine.end(); it++ ) {
- //if ( _tcsicmp("List of devices attached ", it->c_str()) == 0 )
- if ( it->find("List of devices attached") != std::string::npos ||
- it->find("* daemon started successfully") != std::string::npos ||
- it->find("* daemon not running") != std::string::npos )
- continue;
- SATDEV::STDevice stDevice;
- // 设备类型;
- if ( it->find(":5555") != std::string::npos )
- stDevice.nType = SATDEV::Reticle;
- else if ( it->find(":5555") == std::string::npos )
- stDevice.nType = SATDEV::Usb;
- stDevice.ulOfflineTime = 0;
- // 设备状态;
- if ( (npos = it->find(" device")) != std::string::npos )
- stDevice.nStatus = SATDEV::Online;
- else if ( (npos = it->find(" offline")) != std::string::npos )
- stDevice.nStatus = SATDEV::Offline;
- else if ( (npos = it->find("unauthorized")) != std::string::npos ) // 未认证也做为离线的一种;
- {
- stDevice.nStatus = SATDEV::Unauthorized;
- GLOBAL::WriteTextLog(GLOBAL::SAT_DEV,"找到unauthorized状态的设备");
- }
- // 获取设备名;
- stDevice.strName = it->substr(0, npos);
- // 如果是网络,去掉5555;
- if ( stDevice.nType == SATDEV::Reticle ) {
- npos = stDevice.strName.find(":5555");
- stDevice.strName = stDevice.strName.substr(0, npos);
- }
- // 新设备否(一般用于usb设备)
- IsNewDevices(stDevice);
- // 压入容器保存;
- vtDevices.push_back(stDevice);
- }
- }
- void CSATDevices::ReConnectAllDevices()
- {
- std::string str;
- std::vector<SATDEV::STDevice>::iterator it = s_vtDevices.begin();
- for ( ;it != s_vtDevices.end(); it++ ) {
- if ( it->nType == SATDEV::Reticle ) {
- str = "adb connect ";
- str.append(it->strName);
- WinExec(str.c_str(), SW_HIDE);
- }
- }
- }
- void CSATDevices::SetDeviceUsageStatus(std::string strDevName, SATDEV::DEVICE_USAGE_STATUS status)
- {
- AutoThreadSection ats(&s_ThreadSection);
- std::vector<SATDEV::STDevice>::iterator it = s_vtDevices.begin();
- for ( ;it != s_vtDevices.end(); it++ ) {
- if ( _tcsicmp(it->strName.c_str(), strDevName.c_str() ) == 0) {
- it->nUsageState = status;
- break;
- }
- }
- }
- int CSATDevices::AttachDeviceName2Buffer(SATPROTO::Device (&pbuff)[SATPROTO::MAX_DEVS])
- {
- int count = 0;
- if ( pbuff ) {
- std::string str;
- std::vector<SATDEV::STDevice>::iterator it = s_vtDevices.begin();
- for ( ;it != s_vtDevices.end(); it++ ) {
- pbuff[count].nType = it->nType;
- pbuff[count].nStatus = it->nStatus;
- pbuff[count].nUsageState = it->nUsageState;
- memcpy_s(pbuff[count++].szName, MAX_PATH, it->strName.c_str(), it->strName.size());
- // 超过MAX_DEVS退出;
- if ( count == SATPROTO::MAX_DEVS )
- break;
- }
- }
- return count;
- }
- void CSATDevices::SaveDeviceStatus2Config(SATDEV::STDevice &stDevice)
- {
- if ( stDevice.nStatus == SATDEV::Online )
- WritePrivateProfileString(_T("ADBSTATUS"), stDevice.strName.c_str(), _T("Online"), GLOBAL::g_szPython27ServerConfig);
- else if ( stDevice.nStatus == SATDEV::Offline )
- WritePrivateProfileString(_T("ADBSTATUS"), stDevice.strName.c_str(), _T("Offline"), GLOBAL::g_szPython27ServerConfig);
- else if ( stDevice.nStatus == SATDEV::Dropline )
- WritePrivateProfileString(_T("ADBSTATUS"), stDevice.strName.c_str(), _T("Dropline"), GLOBAL::g_szPython27ServerConfig);
- else if ( stDevice.nStatus == SATDEV::Unauthorized)
- WritePrivateProfileString(_T("ADBSTATUS"), stDevice.strName.c_str(), _T("Unauthorized"), GLOBAL::g_szPython27ServerConfig);
- }
- void CSATDevices::SaveAllDevicesStatus2Config()
- {
- // 先清空段内容;
- WritePrivateProfileString(_T("ADBSTATUS"), NULL, NULL, GLOBAL::g_szPython27ServerConfig);
- std::vector<SATDEV::STDevice>::iterator it = s_vtDevices.begin();
- for ( ; it != s_vtDevices.end(); it++ ) {
- if ( it->nType != SATDEV::Virtual )
- SaveDeviceStatus2Config(*it);
- }
- }
- SOCKET CSATDevices::ConnectAdbDevice(std::string ip, int port)
- {
- // 创建套接字;
- SOCKET sock = socket(AF_INET, SOCK_STREAM, 0);
- if( sock == INVALID_SOCKET )
- return INVALID_SOCKET;
- // 设置为非阻塞;
- DWORD ul = 1;
- if( 0 != ioctlsocket( sock, FIONBIO, &ul) )
- return INVALID_SOCKET;
- // 连接服务器;
- SOCKADDR_IN sin;
- sin.sin_family = AF_INET;
- sin.sin_port = htons(port);
- sin.sin_addr.s_addr = inet_addr( ip.c_str() );
- if( ! ( SOCKET_ERROR == connect( sock, (SOCKADDR *)&sin, sizeof(sin)) && WSAGetLastError() == WSAEWOULDBLOCK ) )
- return INVALID_SOCKET;
- return sock;
- }
- bool CSATDevices::CheckAdbSocket(SOCKET sock)
- {
- // 由于异步connect直接返回;
- // 需要用select另外判断;
- fd_set fs_read;
- FD_ZERO( &fs_read );
- FD_SET( sock, &fs_read );
- fd_set fs_write;
- fs_write.fd_count = 1;
- fs_write.fd_array[0] = sock;
- fd_set fs_error;
- fs_error.fd_count = 1;
- fs_error.fd_array[0] = sock;
- timeval tv;
- tv.tv_sec = GLOBAL::g_stSATConfig.ulAsynConnectTimeout/1000000; // 秒;
- tv.tv_usec = GLOBAL::g_stSATConfig.ulAsynConnectTimeout%1000000; // 微秒(百万分之一秒);
- // 必须要设置超时值,否则默认值
- int ret = select( 0, &fs_read, &fs_write, &fs_error, &tv );
- if( ret == SOCKET_ERROR )
- return false;
- // 判断socket句柄是否可写;
- if( !FD_ISSET( sock, &fs_write ) )
- return false;
- int optval = -1;
- int optlen = sizeof(optval);
- ret = getsockopt( sock, SOL_SOCKET,SO_ERROR, (char*)(&optval), &optlen );
- if( ret != 0 || optval != 0)
- return false;
- return true;
- }
- void CSATDevices::CloseAdbSocket(SOCKET sock)
- {
- shutdown(sock, SD_BOTH);
- closesocket(sock);
- }
- DWORD CSATDevices::AddAdbThread(LPVOID lpVoid)
- {
- s_bAutoAddAdbThreadFinished = false;
- int a = 0, b = 0, c = 0, d = 0;
- sscanf_s(GLOBAL::g_stSATConfig.szAdbRouteAddress, _T("%d.%d.%d.%d"), &a, &b, &c, &d);
- char szIPAddress[MAX_PATH] = {0};
- for ( int i = d+1; i < 255; i++ )
- {
- sprintf_s(szIPAddress, "%d.%d.%d.%d", a,b,c,i);
- // TODO: 在此处为应用程序的行为编写代码。
- SOCKET sock = ConnectAdbDevice(szIPAddress, 5555);
- if ( sock != INVALID_SOCKET ) {
- if ( CheckAdbSocket(sock) ) {
- AddReticleDevices(szIPAddress);
- GLOBAL::WriteTextLog(GLOBAL::SAT_DEV,"连接IP=%s:5555成功", szIPAddress);
- }
- else
- GLOBAL::WriteTextLog(GLOBAL::SAT_DEV,"连接IP=%s:5555失败", szIPAddress);
- CloseAdbSocket(sock);
- }
- }
- s_bAutoAddAdbThreadFinished = true;
- return 0;
- }
|