sp_btr.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435
  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. DbgWithLinkForC(LOG_LEVEL_ERROR, LOG_TYPE_SYSTEM,"write boot start fail: %d", GetLastError());
  180. }
  181. FlushFileBuffers(hFile);
  182. CloseHandle(hFile);
  183. hFile = INVALID_HANDLE_VALUE;
  184. return 0;
  185. }
  186. int sp_btr_write_on_shutdown(const char *bootrec_path, sp_btr_context_t *ctx, int reason, int way)
  187. {
  188. int reason_count = 1;
  189. int reason_way_count = 1;
  190. char szCurLog[MAX_PATH] = { 0 };
  191. HANDLE hFile;
  192. char line[MAX_LINE_CHAR] = { 0 };
  193. DWORD dwBytesWritten = 0;
  194. SYSTEMTIME st = { 0 };
  195. BOOL bWriteBootInfo = FALSE;
  196. if (reason == ctx->last_boot_info.shutdown_reason)
  197. reason_count = ctx->last_boot_info.shutdown_reason_cnt + 1;
  198. if (way == ctx->last_boot_info.shutdown_way)
  199. reason_way_count = ctx->last_boot_info.shutdown_way_cnt + 1;
  200. y2k_to_localtime(ctx->cur_start_time, &st);
  201. sprintf(szCurLog, "%s" SPLIT_SLASH_STR "%04d%02d.log", bootrec_path, st.wYear, st.wMonth);
  202. bWriteBootInfo = !ExistsFileA(szCurLog);
  203. hFile = CreateFileA(szCurLog, GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
  204. if (hFile == INVALID_HANDLE_VALUE)
  205. {
  206. DbgWithLinkForC(LOG_LEVEL_ERROR, LOG_TYPE_SYSTEM,"open boot file %s fail: %d", szCurLog, GetLastError());
  207. return -1;
  208. }
  209. if (SetFilePointer(hFile, 0, NULL, FILE_END) == INVALID_SET_FILE_POINTER)
  210. {
  211. DbgWithLinkForC(LOG_LEVEL_ERROR, LOG_TYPE_SYSTEM,"set file pos fail: %d", GetLastError());
  212. CloseHandle(hFile);
  213. return -2;
  214. }
  215. // 20160914: 如果退出和启动对应不是同一个日志文件,需要重写启动信息
  216. if (bWriteBootInfo)
  217. {
  218. 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);
  219. // make startup record line
  220. WriteFile(hFile, line, strlen(line), &dwBytesWritten, NULL);
  221. }
  222. //TODO: 不用加换行符??
  223. sprintf(line, ",0x%X,%d,%d,%d,%d", y2k_time_now(), reason, reason_count, way, reason_way_count);
  224. WriteFile(hFile, line, strlen(line), &dwBytesWritten, NULL);
  225. if (dwBytesWritten == 0)
  226. {
  227. DbgWithLinkForC(LOG_LEVEL_ERROR, LOG_TYPE_SYSTEM,"write reboot rec fail: %d", GetLastError());
  228. }
  229. FlushFileBuffers(hFile);
  230. CloseHandle(hFile);
  231. return 0;
  232. }
  233. // 解析一行记录
  234. static BOOL parse_boot_record_line(const char *pLine, unsigned int before_this_time, sp_btr_reocrd_t **ppRec)
  235. {
  236. DWORD dwStartTime = 0, dwEndTime = 0;
  237. DWORD v1 = 0, v2 = 0, v3 = 0, v4 = 0;
  238. DWORD wReason = 0, wReasonNum = 0, wWay = 0, wWayNum = 0;
  239. int n = 0;
  240. if (get_char_count(pLine, ',') < 1 || get_char_count(pLine, '.') < 3)
  241. {
  242. DbgWithLinkForC(LOG_LEVEL_ERROR, LOG_TYPE_SYSTEM,"invalide rec: [%s]", pLine);
  243. return FALSE;
  244. }
  245. n = sscanf_s(pLine, "0x%X,%d.%d.%d.%d,0x%X,%d,%d,%d,%d", &dwStartTime, &v1, &v2, &v3, &v4,
  246. &dwEndTime, &wReason, &wReasonNum, &wWay, &wWayNum);
  247. if (n == 5 || n == 10)
  248. {
  249. if (dwStartTime >= before_this_time)
  250. return FALSE;
  251. if (*ppRec == NULL)
  252. *ppRec = malloc(sizeof(sp_btr_reocrd_t));
  253. memset(*ppRec, 0, sizeof(sp_btr_reocrd_t));
  254. (*ppRec)->tm_start = dwStartTime;
  255. (*ppRec)->version.major = v1;
  256. (*ppRec)->version.minor = v2;
  257. (*ppRec)->version.revision = v3;
  258. (*ppRec)->version.build = v4;
  259. if (n == 10)
  260. {
  261. (*ppRec)->tm_shutdown = dwEndTime;
  262. (*ppRec)->shutdown_reason = wReason;
  263. (*ppRec)->shutdown_reason_cnt = wReasonNum;
  264. (*ppRec)->shutdown_way = wWay;
  265. (*ppRec)->shutdown_way_cnt = wWayNum;
  266. }
  267. }
  268. return TRUE;
  269. }
  270. // 解析一个日志文件
  271. static sp_btr_reocrd_t* find_rec_from_file_before(const char *pLogName, unsigned int before_this_time)
  272. {
  273. sp_btr_reocrd_t *pRec = NULL;
  274. char szLine[8 * 1024] = { 0 };
  275. int nLeftLen = 0;
  276. HANDLE hFile = INVALID_HANDLE_VALUE;
  277. BOOL bHasLeft = TRUE;
  278. hFile = CreateFileA(pLogName, GENERIC_READ, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
  279. if (hFile == INVALID_HANDLE_VALUE)
  280. return NULL;
  281. while (bHasLeft)
  282. {
  283. DWORD nReadLen = 0;
  284. char *pStart = szLine;
  285. BOOL bRet = ReadFile(hFile, pStart + nLeftLen, sizeof(szLine)-nLeftLen - 1, &nReadLen, NULL);
  286. if (!bRet || nReadLen == 0)
  287. break;
  288. if (nReadLen < sizeof(szLine)-nLeftLen - 1)
  289. bHasLeft = FALSE;
  290. nLeftLen += nReadLen;
  291. szLine[nLeftLen] = 0;
  292. // 解析行
  293. while (TRUE)
  294. {
  295. char *p = strstr(pStart, "\r\n");
  296. if (p == NULL && bHasLeft)
  297. break;
  298. if (p != NULL)
  299. {
  300. *p = 0;
  301. *(p + 1) = 0;
  302. }
  303. // 返回失败(启动时间超出或格式错误)则返回
  304. if (!parse_boot_record_line(pStart, before_this_time, &pRec))
  305. goto RETURN;
  306. if (p != NULL)
  307. {
  308. nLeftLen -= p + 2 - pStart;
  309. pStart = p + 2;
  310. }
  311. else
  312. goto RETURN;
  313. }
  314. // 重新移动数据
  315. if (szLine != pStart && nLeftLen > 0)
  316. {
  317. memmove_s(szLine, sizeof(szLine), pStart, nLeftLen);
  318. memset(szLine + nLeftLen, 0, sizeof(szLine)-nLeftLen); // memory set zero
  319. }
  320. }
  321. RETURN:
  322. CloseHandle(hFile);
  323. return pRec;
  324. }
  325. sp_btr_reocrd_t* sp_btr_get_rec_before(const char *bootrec_path, sp_btr_context_t *ctx, unsigned int before_this_time)
  326. {
  327. SYSTEMTIME st = { 0 };
  328. char szBootLog[MAX_PATH] = { 0 };
  329. sp_btr_reocrd_t *pRec = NULL;
  330. int nTryCount = 0;
  331. // 返回上次重启记录
  332. if (before_this_time == 0)
  333. {
  334. if (ctx->last_boot_info.tm_start == 0)
  335. return NULL;
  336. pRec = (sp_btr_reocrd_t*)malloc(sizeof(sp_btr_reocrd_t));
  337. *pRec = ctx->last_boot_info;
  338. return pRec;
  339. }
  340. y2k_to_localtime(before_this_time, &st);
  341. while (nTryCount++ <= 12)
  342. {
  343. sprintf(szBootLog, "%s" SPLIT_SLASH_STR "%04d%02d.log", bootrec_path, st.wYear, st.wMonth);
  344. if (!ExistsFileA(szBootLog))
  345. {
  346. DbgWithLinkForC(LOG_LEVEL_ERROR, LOG_TYPE_SYSTEM,"boot log %s not exists", szBootLog);
  347. }
  348. else
  349. {
  350. DbgWithLinkForC(LOG_LEVEL_INFO, LOG_TYPE_SYSTEM, "parse log %s now", szBootLog);
  351. pRec = find_rec_from_file_before(szBootLog, before_this_time);
  352. if (pRec != NULL)
  353. return pRec;
  354. }
  355. if (st.wMonth > 1)
  356. st.wMonth--;
  357. else
  358. {
  359. st.wMonth = 12;
  360. st.wYear--;
  361. }
  362. }
  363. return NULL;
  364. }