sp_btr.c 11 KB

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