sp_btr.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444
  1. #include "precompile.h"
  2. #include <winpr/sysinfo.h>
  3. #include <winpr/handle.h>
  4. #include "sp_btr.h"
  5. #include "fileutil.h"
  6. #include "memutil.h"
  7. #include "shm_mem.h"
  8. #include "y2k_time.h"
  9. #include "SpBase.h"
  10. #include "sp_logwithlinkforc.h"
  11. #include <winpr/string.h>
  12. #include <winpr/wtypes.h>
  13. #include <winpr/file.h>
  14. #ifndef _WIN32
  15. #define min(a,b) (((a) < (b)) ? (a) : (b))
  16. #endif //NOT _WIN32
  17. #define MAX_LINE_CHAR 256
  18. // 1. 不需要补充[]关机没填写的部分,读取的时候默认为死机即可
  19. // 2. 写文件的时候该名称.tmp,写完再改回.log,
  20. // 3. 如果写得过程中停电关机,那文件肯定停留在.tmp的状态,因此当系统后面检测到.tmp文件时必须删除
  21. // 4. 系统使用ups电源,出现3的情况很少很少
  22. static int get_char_count(const char *s, char ch)
  23. {
  24. int cnt = 0;
  25. while (*s != 0) {
  26. if (*s == ch)
  27. cnt++;
  28. s++;
  29. }
  30. return cnt;
  31. }
  32. int sp_btr_write_on_start(const char *bootrec_path, sp_version_t *version, sp_btr_context_t **p_ctx)
  33. {
  34. SYSTEMTIME st;
  35. char szCurLog[MAX_PATH] = { 0 };
  36. char szPreLog[MAX_PATH] = { 0 };
  37. HANDLE hFile = INVALID_HANDLE_VALUE;
  38. char line[MAX_LINE_CHAR] = { 0 };
  39. char *pStart;
  40. DWORD nMoveLen;
  41. LONG negMoveLen;
  42. int nReadLen = 0;
  43. char *pTemp = NULL;
  44. DWORD dwStartTime = 0, dwEndTime = 0;
  45. DWORD v1 = 0, v2 = 0, v3 = 0, v4 = 0;
  46. DWORD wReason = 0, wReasonNum = 0, wWay = 0, wWayNum = 0;
  47. int n = 0;
  48. BOOL bFirst;
  49. DWORD dwBytesWritten = 0;
  50. if (!ExistsDirA(bootrec_path)) {
  51. CreateDirA(bootrec_path, FALSE);
  52. }
  53. GetLocalTime(&st);
  54. sprintf(szCurLog, "%s" SPLIT_SLASH_STR "%04d%02d.log", bootrec_path, st.wYear, st.wMonth);
  55. *p_ctx = (sp_btr_context_t*)shm_malloc(sizeof(sp_btr_context_t));
  56. memset(*p_ctx, 0, sizeof(sp_btr_context_t));
  57. (*p_ctx)->cur_start_time = y2k_time_now();
  58. (*p_ctx)->cur_version = *version;
  59. if (st.wMonth > 1) {
  60. st.wMonth--;
  61. }
  62. else {
  63. st.wMonth = 12;
  64. st.wYear--;
  65. }
  66. sprintf(szPreLog, "%s" SPLIT_SLASH_STR "%04d%02d.log", bootrec_path, st.wYear, st.wMonth);
  67. // read last reboot way & reason from boot log
  68. if (ExistsFileA(szCurLog))
  69. hFile = CreateFileA(szCurLog, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
  70. else if (ExistsFileA(szPreLog))
  71. hFile = CreateFileA(szPreLog, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
  72. else
  73. {
  74. goto WriteLog;
  75. }
  76. if (hFile == INVALID_HANDLE_VALUE)
  77. {
  78. DbgWithLinkForC(LOG_LEVEL_ERROR, LOG_TYPE_SYSTEM,"open boot log file fail");
  79. return -11;
  80. }
  81. // read last line
  82. nMoveLen = min(GetFileSize(hFile, NULL), MAX_LINE_CHAR-1);
  83. negMoveLen = -((LONG)nMoveLen);
  84. if (SetFilePointer(hFile, negMoveLen, NULL, FILE_END) == INVALID_SET_FILE_POINTER)
  85. {
  86. DbgWithLinkForC(LOG_LEVEL_ERROR, LOG_TYPE_SYSTEM,"set file pos fail: %d", GetLastError());
  87. CloseHandle(hFile);
  88. return -2;
  89. }
  90. if (!ReadFile(hFile, line, nMoveLen, &nReadLen, NULL) || nReadLen < nMoveLen)
  91. {
  92. DbgWithLinkForC(LOG_LEVEL_ERROR, LOG_TYPE_SYSTEM,"read boot log fail: %d", GetLastError());
  93. CloseHandle(hFile);
  94. return -3;
  95. }
  96. CloseHandle(hFile);
  97. pStart = line;
  98. do
  99. {
  100. /** TODO: [Gifur@20201123]*/
  101. #ifdef _WIN32
  102. pTemp = strstr(pStart, "\r\n");
  103. if (pTemp != NULL)
  104. pStart = pTemp + 2;
  105. #else
  106. pTemp = strstr(pStart, "\n");
  107. if (pTemp != NULL)
  108. pStart = pTemp + 1;
  109. #endif //_WIN32
  110. } while (pTemp != NULL);
  111. // 20160914: 解析异常时,不退出,换行写入当前启动日志
  112. if (get_char_count(pStart, ',') < 1 || get_char_count(pStart, '.') < 3)
  113. {
  114. DbgWithLinkForC(LOG_LEVEL_ERROR, LOG_TYPE_SYSTEM,"invalide rec: [%s]", pStart);
  115. //return -31;
  116. }
  117. else
  118. {
  119. n = sscanf_s(pStart, "0x%X,%d.%d.%d.%d,0x%X,%d,%d,%d,%d", &dwStartTime, &v1, &v2, &v3, &v4,
  120. &dwEndTime, &wReason, &wReasonNum, &wWay, &wWayNum);
  121. if (n != 5 && n != 10)
  122. {
  123. DbgWithLinkForC(LOG_LEVEL_ERROR, LOG_TYPE_SYSTEM,"parse boot rec fail: [%s](%d)", pStart, n);
  124. #ifndef _WIN32
  125. DbgWithLinkForC(LOG_LEVEL_ERROR, LOG_TYPE_SYSTEM,"strerror: %s", strerror(errno));
  126. #endif //NOT _WIN32
  127. }
  128. else
  129. {
  130. (*p_ctx)->last_boot_info.tm_start = dwStartTime;
  131. (*p_ctx)->last_boot_info.version.major = v1;
  132. (*p_ctx)->last_boot_info.version.minor = v2;
  133. (*p_ctx)->last_boot_info.version.revision = v3;
  134. (*p_ctx)->last_boot_info.version.build = v4;
  135. if (n == 5)
  136. {
  137. DbgWithLinkForC(LOG_LEVEL_WARN, LOG_TYPE_SYSTEM,"last shutdown time & reason unknown");
  138. }
  139. else if (n == 10)
  140. {
  141. char tmp[64] = { 0 };
  142. y2k_to_string(dwEndTime, tmp, sizeof(tmp));
  143. /** TODO: convert number to plain text [Gifur@20201123]*/
  144. DbgWithLinkForC(LOG_LEVEL_INFO, LOG_TYPE_SYSTEM,"last shutdown at %s, reason: %d, way: %d", tmp, wReason, wWay);
  145. (*p_ctx)->last_boot_info.tm_shutdown = dwEndTime;
  146. (*p_ctx)->last_boot_info.shutdown_reason = wReason;
  147. (*p_ctx)->last_boot_info.shutdown_reason_cnt = wReasonNum;
  148. (*p_ctx)->last_boot_info.shutdown_way = wWay;
  149. (*p_ctx)->last_boot_info.shutdown_way_cnt = wWayNum;
  150. }
  151. }
  152. }
  153. WriteLog:
  154. // write boot start record
  155. bFirst = !ExistsFileA(szCurLog);
  156. hFile = CreateFileA(szCurLog, GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
  157. if (hFile == INVALID_HANDLE_VALUE)
  158. {
  159. DbgWithLinkForC(LOG_LEVEL_ERROR, LOG_TYPE_SYSTEM, "open boot file fail: %s(%d)(%d)", szCurLog, bFirst, GetLastError());
  160. return -5;
  161. }
  162. if (SetFilePointer(hFile, 0, NULL, FILE_END) == INVALID_SET_FILE_POINTER)
  163. {
  164. DbgWithLinkForC(LOG_LEVEL_ERROR, LOG_TYPE_SYSTEM,"set file pos fail: %d", GetLastError());
  165. CloseHandle(hFile);
  166. return -51;
  167. }
  168. memset(line, 0, sizeof(line));
  169. #ifdef _WIN32
  170. sprintf(line, "%s0x%X,%d.%d.%d.%d", bFirst ? "" : "\r\n",
  171. #else
  172. sprintf(line, "%s0x%X,%d.%d.%d.%d", bFirst ? "" : "\n",
  173. #endif //_WIN32
  174. (*p_ctx)->cur_start_time, version->major, version->minor, version->revision, version->build);
  175. // make startup record line
  176. WriteFile(hFile, line, strlen(line), &dwBytesWritten, NULL);
  177. if (dwBytesWritten == 0)
  178. {
  179. char szTmp[512] = { 0 };
  180. GetSystemErrorDesc(GetLastError(), szTmp, sizeof(szTmp));
  181. DbgWithLinkForC(LOG_LEVEL_ERROR, LOG_TYPE_SYSTEM,"write boot start fail: %s", szTmp);
  182. }
  183. FlushFileBuffers(hFile);
  184. CloseHandle(hFile);
  185. hFile = INVALID_HANDLE_VALUE;
  186. return 0;
  187. }
  188. int sp_btr_write_on_shutdown(const char *bootrec_path, sp_btr_context_t *ctx, int reason, int way)
  189. {
  190. int reason_count = 1;
  191. int reason_way_count = 1;
  192. char szCurLog[MAX_PATH] = { 0 };
  193. HANDLE hFile;
  194. char line[MAX_LINE_CHAR] = { 0 };
  195. DWORD dwBytesWritten = 0;
  196. SYSTEMTIME st = { 0 };
  197. BOOL bWriteBootInfo = FALSE;
  198. if (reason == ctx->last_boot_info.shutdown_reason)
  199. reason_count = ctx->last_boot_info.shutdown_reason_cnt + 1;
  200. if (way == ctx->last_boot_info.shutdown_way)
  201. reason_way_count = ctx->last_boot_info.shutdown_way_cnt + 1;
  202. y2k_to_localtime(ctx->cur_start_time, &st);
  203. sprintf(szCurLog, "%s" SPLIT_SLASH_STR "%04d%02d.log", bootrec_path, st.wYear, st.wMonth);
  204. bWriteBootInfo = !ExistsFileA(szCurLog);
  205. hFile = CreateFileA(szCurLog, GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
  206. if (hFile == INVALID_HANDLE_VALUE)
  207. {
  208. DbgWithLinkForC(LOG_LEVEL_ERROR, LOG_TYPE_SYSTEM,"open boot file %s fail: %d", szCurLog, GetLastError());
  209. return -1;
  210. }
  211. if (SetFilePointer(hFile, 0, NULL, FILE_END) == INVALID_SET_FILE_POINTER)
  212. {
  213. DbgWithLinkForC(LOG_LEVEL_ERROR, LOG_TYPE_SYSTEM,"set file pos fail: %d", GetLastError());
  214. CloseHandle(hFile);
  215. return -2;
  216. }
  217. // 20160914: 如果退出和启动对应不是同一个日志文件,需要重写启动信息
  218. if (bWriteBootInfo)
  219. {
  220. 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);
  221. // make startup record line
  222. WriteFile(hFile, line, strlen(line), &dwBytesWritten, NULL);
  223. }
  224. //TODO: 不用加换行符??
  225. sprintf(line, ",0x%X,%d,%d,%d,%d", y2k_time_now(), reason, reason_count, way, reason_way_count);
  226. WriteFile(hFile, line, strlen(line), &dwBytesWritten, NULL);
  227. if (dwBytesWritten == 0)
  228. {
  229. char szTmp[512] = { 0 };
  230. GetSystemErrorDesc(GetLastError(), szTmp, sizeof(szTmp));
  231. DbgWithLinkForC(LOG_LEVEL_ERROR, LOG_TYPE_SYSTEM,"write reboot rec fail: %s", szTmp);
  232. }
  233. FlushFileBuffers(hFile);
  234. CloseHandle(hFile);
  235. return 0;
  236. }
  237. // 解析一行记录
  238. static BOOL parse_boot_record_line(const char *pLine, unsigned int before_this_time, sp_btr_reocrd_t **ppRec)
  239. {
  240. DWORD dwStartTime = 0, dwEndTime = 0;
  241. DWORD v1 = 0, v2 = 0, v3 = 0, v4 = 0;
  242. DWORD wReason = 0, wReasonNum = 0, wWay = 0, wWayNum = 0;
  243. int n = 0;
  244. if (get_char_count(pLine, ',') < 1 || get_char_count(pLine, '.') < 3)
  245. {
  246. DbgWithLinkForC(LOG_LEVEL_ERROR, LOG_TYPE_SYSTEM,"invalide rec: [%s]", pLine);
  247. return FALSE;
  248. }
  249. n = sscanf_s(pLine, "0x%X,%d.%d.%d.%d,0x%X,%d,%d,%d,%d", &dwStartTime, &v1, &v2, &v3, &v4,
  250. &dwEndTime, &wReason, &wReasonNum, &wWay, &wWayNum);
  251. if (n == 5 || n == 10)
  252. {
  253. if (dwStartTime >= before_this_time)
  254. return FALSE;
  255. if (*ppRec == NULL)
  256. *ppRec = malloc(sizeof(sp_btr_reocrd_t));
  257. memset(*ppRec, 0, sizeof(sp_btr_reocrd_t));
  258. (*ppRec)->tm_start = dwStartTime;
  259. (*ppRec)->version.major = v1;
  260. (*ppRec)->version.minor = v2;
  261. (*ppRec)->version.revision = v3;
  262. (*ppRec)->version.build = v4;
  263. if (n == 10)
  264. {
  265. (*ppRec)->tm_shutdown = dwEndTime;
  266. (*ppRec)->shutdown_reason = wReason;
  267. (*ppRec)->shutdown_reason_cnt = wReasonNum;
  268. (*ppRec)->shutdown_way = wWay;
  269. (*ppRec)->shutdown_way_cnt = wWayNum;
  270. }
  271. }
  272. return TRUE;
  273. }
  274. // 解析一个日志文件
  275. static sp_btr_reocrd_t* find_rec_from_file_before(const char *pLogName, unsigned int before_this_time)
  276. {
  277. sp_btr_reocrd_t *pRec = NULL;
  278. char szLine[8 * 1024] = { 0 };
  279. int nLeftLen = 0;
  280. HANDLE hFile = INVALID_HANDLE_VALUE;
  281. BOOL bHasLeft = TRUE;
  282. hFile = CreateFileA(pLogName, GENERIC_READ, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
  283. if (hFile == INVALID_HANDLE_VALUE)
  284. return NULL;
  285. while (bHasLeft)
  286. {
  287. DWORD nReadLen = 0;
  288. char *pStart = szLine;
  289. BOOL bRet = ReadFile(hFile, pStart + nLeftLen, sizeof(szLine)-nLeftLen - 1, &nReadLen, NULL);
  290. if (!bRet || nReadLen == 0)
  291. break;
  292. if (nReadLen < sizeof(szLine)-nLeftLen - 1)
  293. bHasLeft = FALSE;
  294. nLeftLen += nReadLen;
  295. szLine[nLeftLen] = 0;
  296. // 解析行
  297. while (TRUE)
  298. {
  299. char *p = strstr(pStart, "\r\n");
  300. if (p == NULL && bHasLeft)
  301. break;
  302. if (p != NULL)
  303. {
  304. *p = 0;
  305. *(p + 1) = 0;
  306. }
  307. // 返回失败(启动时间超出或格式错误)则返回
  308. if (!parse_boot_record_line(pStart, before_this_time, &pRec))
  309. goto RETURN;
  310. if (p != NULL)
  311. {
  312. nLeftLen -= p + 2 - pStart;
  313. pStart = p + 2;
  314. }
  315. else
  316. goto RETURN;
  317. }
  318. // 重新移动数据
  319. if (szLine != pStart && nLeftLen > 0)
  320. {
  321. memmove_s(szLine, sizeof(szLine), pStart, nLeftLen);
  322. memset(szLine + nLeftLen, 0, sizeof(szLine)-nLeftLen); // memory set zero
  323. }
  324. }
  325. RETURN:
  326. CloseHandle(hFile);
  327. return pRec;
  328. }
  329. sp_btr_reocrd_t* sp_btr_get_rec_before(const char *bootrec_path, sp_btr_context_t *ctx, unsigned int before_this_time)
  330. {
  331. SYSTEMTIME st = { 0 };
  332. char szBootLog[MAX_PATH] = { 0 };
  333. sp_btr_reocrd_t *pRec = NULL;
  334. int nTryCount = 0;
  335. // 返回上次重启记录
  336. if (before_this_time == 0)
  337. {
  338. if (ctx->last_boot_info.tm_start == 0)
  339. return NULL;
  340. pRec = (sp_btr_reocrd_t*)malloc(sizeof(sp_btr_reocrd_t));
  341. *pRec = ctx->last_boot_info;
  342. return pRec;
  343. }
  344. y2k_to_localtime(before_this_time, &st);
  345. while (nTryCount++ <= 12)
  346. {
  347. #ifdef _WIN32
  348. sprintf(szBootLog, "%s\\%04d%02d.log", bootrec_path, st.wYear, st.wMonth);
  349. #else
  350. sprintf(szBootLog, "%s/%04d%02d.log", bootrec_path, st.wYear, st.wMonth);
  351. #endif
  352. if (!ExistsFileA(szBootLog))
  353. {
  354. DbgWithLinkForC(LOG_LEVEL_ERROR, LOG_TYPE_SYSTEM,"boot log %s not exists", szBootLog);
  355. }
  356. else
  357. {
  358. DbgWithLinkForC(LOG_LEVEL_INFO, LOG_TYPE_SYSTEM, "parse log %s now", szBootLog);
  359. pRec = find_rec_from_file_before(szBootLog, before_this_time);
  360. if (pRec != NULL)
  361. return pRec;
  362. }
  363. if (st.wMonth > 1)
  364. st.wMonth--;
  365. else
  366. {
  367. st.wMonth = 12;
  368. st.wYear--;
  369. }
  370. }
  371. return NULL;
  372. }