#include "precompile.h" #include #include #include "sp_btr.h" #include "fileutil.h" #include "memutil.h" #include "shm_mem.h" #include "y2k_time.h" #include "SpBase.h" #include "sp_logwithlinkforc.h" #include #include #include #ifndef _WIN32 #define min(a,b) (((a) < (b)) ? (a) : (b)) #endif //NOT _WIN32 #define MAX_LINE_CHAR 256 // 1. 不需要补充[]关机没填写的部分,读取的时候默认为死机即可 // 2. 写文件的时候该名称.tmp,写完再改回.log, // 3. 如果写得过程中停电关机,那文件肯定停留在.tmp的状态,因此当系统后面检测到.tmp文件时必须删除 // 4. 系统使用ups电源,出现3的情况很少很少 static int get_char_count(const char *s, char ch) { int cnt = 0; while (*s != 0) { if (*s == ch) cnt++; s++; } return cnt; } int sp_btr_write_on_start(const char *bootrec_path, sp_version_t *version, sp_btr_context_t **p_ctx) { SYSTEMTIME st; char szCurLog[MAX_PATH] = { 0 }; char szPreLog[MAX_PATH] = { 0 }; HANDLE hFile = INVALID_HANDLE_VALUE; char line[MAX_LINE_CHAR] = { 0 }; char *pStart; DWORD nMoveLen; LONG negMoveLen; int nReadLen = 0; char *pTemp = NULL; DWORD dwStartTime = 0, dwEndTime = 0; DWORD v1 = 0, v2 = 0, v3 = 0, v4 = 0; DWORD wReason = 0, wReasonNum = 0, wWay = 0, wWayNum = 0; int n = 0; BOOL bFirst; DWORD dwBytesWritten = 0; if (!ExistsDirA(bootrec_path)) { CreateDirA(bootrec_path, FALSE); } GetLocalTime(&st); sprintf(szCurLog, "%s" SPLIT_SLASH_STR "%04d%02d.log", bootrec_path, st.wYear, st.wMonth); *p_ctx = (sp_btr_context_t*)shm_malloc(sizeof(sp_btr_context_t)); memset(*p_ctx, 0, sizeof(sp_btr_context_t)); (*p_ctx)->cur_start_time = y2k_time_now(); (*p_ctx)->cur_version = *version; if (st.wMonth > 1) { st.wMonth--; } else { st.wMonth = 12; st.wYear--; } sprintf(szPreLog, "%s" SPLIT_SLASH_STR "%04d%02d.log", bootrec_path, st.wYear, st.wMonth); // read last reboot way & reason from boot log if (ExistsFileA(szCurLog)) hFile = CreateFileA(szCurLog, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); else if (ExistsFileA(szPreLog)) hFile = CreateFileA(szPreLog, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); else { goto WriteLog; } if (hFile == INVALID_HANDLE_VALUE) { DbgWithLinkForC(LOG_LEVEL_ERROR, LOG_TYPE_SYSTEM,"open boot log file fail"); return -11; } // read last line nMoveLen = min(GetFileSize(hFile, NULL), MAX_LINE_CHAR-1); negMoveLen = -((LONG)nMoveLen); if (SetFilePointer(hFile, negMoveLen, NULL, FILE_END) == INVALID_SET_FILE_POINTER) { DbgWithLinkForC(LOG_LEVEL_ERROR, LOG_TYPE_SYSTEM,"set file pos fail: %d", GetLastError()); CloseHandle(hFile); return -2; } if (!ReadFile(hFile, line, nMoveLen, &nReadLen, NULL) || nReadLen < nMoveLen) { DbgWithLinkForC(LOG_LEVEL_ERROR, LOG_TYPE_SYSTEM,"read boot log fail: %d", GetLastError()); CloseHandle(hFile); return -3; } CloseHandle(hFile); pStart = line; do { /** TODO: [Gifur@20201123]*/ #ifdef _WIN32 pTemp = strstr(pStart, "\r\n"); if (pTemp != NULL) pStart = pTemp + 2; #else pTemp = strstr(pStart, "\n"); if (pTemp != NULL) pStart = pTemp + 1; #endif //_WIN32 } while (pTemp != NULL); // 20160914: 解析异常时,不退出,换行写入当前启动日志 if (get_char_count(pStart, ',') < 1 || get_char_count(pStart, '.') < 3) { DbgWithLinkForC(LOG_LEVEL_ERROR, LOG_TYPE_SYSTEM,"invalide rec: [%s]", pStart); //return -31; } else { n = sscanf_s(pStart, "0x%X,%d.%d.%d.%d,0x%X,%d,%d,%d,%d", &dwStartTime, &v1, &v2, &v3, &v4, &dwEndTime, &wReason, &wReasonNum, &wWay, &wWayNum); if (n != 5 && n != 10) { DbgWithLinkForC(LOG_LEVEL_ERROR, LOG_TYPE_SYSTEM,"parse boot rec fail: [%s](%d)", pStart, n); #ifndef _WIN32 DbgWithLinkForC(LOG_LEVEL_ERROR, LOG_TYPE_SYSTEM,"strerror: %s", strerror(errno)); #endif //NOT _WIN32 } else { (*p_ctx)->last_boot_info.tm_start = dwStartTime; (*p_ctx)->last_boot_info.version.major = v1; (*p_ctx)->last_boot_info.version.minor = v2; (*p_ctx)->last_boot_info.version.revision = v3; (*p_ctx)->last_boot_info.version.build = v4; if (n == 5) { DbgWithLinkForC(LOG_LEVEL_WARN, LOG_TYPE_SYSTEM,"last shutdown time & reason unknown"); } else if (n == 10) { char tmp[64] = { 0 }; y2k_to_string(dwEndTime, tmp, sizeof(tmp)); /** TODO: convert number to plain text [Gifur@20201123]*/ DbgWithLinkForC(LOG_LEVEL_INFO, LOG_TYPE_SYSTEM,"last shutdown at %s, reason: %d, way: %d", tmp, wReason, wWay); (*p_ctx)->last_boot_info.tm_shutdown = dwEndTime; (*p_ctx)->last_boot_info.shutdown_reason = wReason; (*p_ctx)->last_boot_info.shutdown_reason_cnt = wReasonNum; (*p_ctx)->last_boot_info.shutdown_way = wWay; (*p_ctx)->last_boot_info.shutdown_way_cnt = wWayNum; } } } WriteLog: // write boot start record bFirst = !ExistsFileA(szCurLog); hFile = CreateFileA(szCurLog, GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile == INVALID_HANDLE_VALUE) { DbgWithLinkForC(LOG_LEVEL_ERROR, LOG_TYPE_SYSTEM, "open boot file fail: %s(%d)(%d)", szCurLog, bFirst, GetLastError()); return -5; } if (SetFilePointer(hFile, 0, NULL, FILE_END) == INVALID_SET_FILE_POINTER) { DbgWithLinkForC(LOG_LEVEL_ERROR, LOG_TYPE_SYSTEM,"set file pos fail: %d", GetLastError()); CloseHandle(hFile); return -51; } memset(line, 0, sizeof(line)); #ifdef _WIN32 sprintf(line, "%s0x%X,%d.%d.%d.%d", bFirst ? "" : "\r\n", #else sprintf(line, "%s0x%X,%d.%d.%d.%d", bFirst ? "" : "\n", #endif //_WIN32 (*p_ctx)->cur_start_time, version->major, version->minor, version->revision, version->build); // make startup record line WriteFile(hFile, line, strlen(line), &dwBytesWritten, NULL); if (dwBytesWritten == 0) { DbgWithLinkForC(LOG_LEVEL_ERROR, LOG_TYPE_SYSTEM,"write boot start fail: %d", GetLastError()); } FlushFileBuffers(hFile); CloseHandle(hFile); hFile = INVALID_HANDLE_VALUE; return 0; } int sp_btr_write_on_shutdown(const char *bootrec_path, sp_btr_context_t *ctx, int reason, int way) { int reason_count = 1; int reason_way_count = 1; char szCurLog[MAX_PATH] = { 0 }; HANDLE hFile; char line[MAX_LINE_CHAR] = { 0 }; DWORD dwBytesWritten = 0; SYSTEMTIME st = { 0 }; BOOL bWriteBootInfo = FALSE; if (reason == ctx->last_boot_info.shutdown_reason) reason_count = ctx->last_boot_info.shutdown_reason_cnt + 1; if (way == ctx->last_boot_info.shutdown_way) reason_way_count = ctx->last_boot_info.shutdown_way_cnt + 1; y2k_to_localtime(ctx->cur_start_time, &st); sprintf(szCurLog, "%s" SPLIT_SLASH_STR "%04d%02d.log", bootrec_path, st.wYear, st.wMonth); bWriteBootInfo = !ExistsFileA(szCurLog); hFile = CreateFileA(szCurLog, GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile == INVALID_HANDLE_VALUE) { DbgWithLinkForC(LOG_LEVEL_ERROR, LOG_TYPE_SYSTEM,"open boot file %s fail: %d", szCurLog, GetLastError()); return -1; } if (SetFilePointer(hFile, 0, NULL, FILE_END) == INVALID_SET_FILE_POINTER) { DbgWithLinkForC(LOG_LEVEL_ERROR, LOG_TYPE_SYSTEM,"set file pos fail: %d", GetLastError()); CloseHandle(hFile); return -2; } // 20160914: 如果退出和启动对应不是同一个日志文件,需要重写启动信息 if (bWriteBootInfo) { sprintf(line, "0x%X,%d.%d.%d.%d", ctx->cur_start_time, ctx->cur_version.major, ctx->cur_version.minor, ctx->cur_version.revision, ctx->cur_version.build); // make startup record line WriteFile(hFile, line, strlen(line), &dwBytesWritten, NULL); } //TODO: 不用加换行符?? sprintf(line, ",0x%X,%d,%d,%d,%d", y2k_time_now(), reason, reason_count, way, reason_way_count); WriteFile(hFile, line, strlen(line), &dwBytesWritten, NULL); if (dwBytesWritten == 0) { DbgWithLinkForC(LOG_LEVEL_ERROR, LOG_TYPE_SYSTEM,"write reboot rec fail: %d", GetLastError()); } FlushFileBuffers(hFile); CloseHandle(hFile); return 0; } // 解析一行记录 static BOOL parse_boot_record_line(const char *pLine, unsigned int before_this_time, sp_btr_reocrd_t **ppRec) { DWORD dwStartTime = 0, dwEndTime = 0; DWORD v1 = 0, v2 = 0, v3 = 0, v4 = 0; DWORD wReason = 0, wReasonNum = 0, wWay = 0, wWayNum = 0; int n = 0; if (get_char_count(pLine, ',') < 1 || get_char_count(pLine, '.') < 3) { DbgWithLinkForC(LOG_LEVEL_ERROR, LOG_TYPE_SYSTEM,"invalide rec: [%s]", pLine); return FALSE; } n = sscanf_s(pLine, "0x%X,%d.%d.%d.%d,0x%X,%d,%d,%d,%d", &dwStartTime, &v1, &v2, &v3, &v4, &dwEndTime, &wReason, &wReasonNum, &wWay, &wWayNum); if (n == 5 || n == 10) { if (dwStartTime >= before_this_time) return FALSE; if (*ppRec == NULL) *ppRec = malloc(sizeof(sp_btr_reocrd_t)); memset(*ppRec, 0, sizeof(sp_btr_reocrd_t)); (*ppRec)->tm_start = dwStartTime; (*ppRec)->version.major = v1; (*ppRec)->version.minor = v2; (*ppRec)->version.revision = v3; (*ppRec)->version.build = v4; if (n == 10) { (*ppRec)->tm_shutdown = dwEndTime; (*ppRec)->shutdown_reason = wReason; (*ppRec)->shutdown_reason_cnt = wReasonNum; (*ppRec)->shutdown_way = wWay; (*ppRec)->shutdown_way_cnt = wWayNum; } } return TRUE; } // 解析一个日志文件 static sp_btr_reocrd_t* find_rec_from_file_before(const char *pLogName, unsigned int before_this_time) { sp_btr_reocrd_t *pRec = NULL; char szLine[8 * 1024] = { 0 }; int nLeftLen = 0; HANDLE hFile = INVALID_HANDLE_VALUE; BOOL bHasLeft = TRUE; hFile = CreateFileA(pLogName, GENERIC_READ, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile == INVALID_HANDLE_VALUE) return NULL; while (bHasLeft) { DWORD nReadLen = 0; char *pStart = szLine; BOOL bRet = ReadFile(hFile, pStart + nLeftLen, sizeof(szLine)-nLeftLen - 1, &nReadLen, NULL); if (!bRet || nReadLen == 0) break; if (nReadLen < sizeof(szLine)-nLeftLen - 1) bHasLeft = FALSE; nLeftLen += nReadLen; szLine[nLeftLen] = 0; // 解析行 while (TRUE) { char *p = strstr(pStart, "\r\n"); if (p == NULL && bHasLeft) break; if (p != NULL) { *p = 0; *(p + 1) = 0; } // 返回失败(启动时间超出或格式错误)则返回 if (!parse_boot_record_line(pStart, before_this_time, &pRec)) goto RETURN; if (p != NULL) { nLeftLen -= p + 2 - pStart; pStart = p + 2; } else goto RETURN; } // 重新移动数据 if (szLine != pStart && nLeftLen > 0) { memmove_s(szLine, sizeof(szLine), pStart, nLeftLen); memset(szLine + nLeftLen, 0, sizeof(szLine)-nLeftLen); // memory set zero } } RETURN: CloseHandle(hFile); return pRec; } sp_btr_reocrd_t* sp_btr_get_rec_before(const char *bootrec_path, sp_btr_context_t *ctx, unsigned int before_this_time) { SYSTEMTIME st = { 0 }; char szBootLog[MAX_PATH] = { 0 }; sp_btr_reocrd_t *pRec = NULL; int nTryCount = 0; // 返回上次重启记录 if (before_this_time == 0) { if (ctx->last_boot_info.tm_start == 0) return NULL; pRec = (sp_btr_reocrd_t*)malloc(sizeof(sp_btr_reocrd_t)); *pRec = ctx->last_boot_info; return pRec; } y2k_to_localtime(before_this_time, &st); while (nTryCount++ <= 12) { sprintf(szBootLog, "%s" SPLIT_SLASH_STR "%04d%02d.log", bootrec_path, st.wYear, st.wMonth); if (!ExistsFileA(szBootLog)) { DbgWithLinkForC(LOG_LEVEL_ERROR, LOG_TYPE_SYSTEM,"boot log %s not exists", szBootLog); } else { DbgWithLinkForC(LOG_LEVEL_INFO, LOG_TYPE_SYSTEM, "parse log %s now", szBootLog); pRec = find_rec_from_file_before(szBootLog, before_this_time); if (pRec != NULL) return pRec; } if (st.wMonth > 1) st.wMonth--; else { st.wMonth = 12; st.wYear--; } } return NULL; }