* tar xzf utvpn-src-unix-v101-7101-public-2010.06.27.tar.gz
[lab.git] / utvpn / utvpn-unix-v101-7101-public / src / Cedar / Logging.c
1 // SoftEther UT-VPN SourceCode\r
2 // \r
3 // Copyright (C) 2004-2010 SoftEther Corporation.\r
4 // Copyright (C) 2004-2010 University of Tsukuba, Japan.\r
5 // Copyright (C) 2003-2010 Daiyuu Nobori.\r
6 // All Rights Reserved.\r
7 // \r
8 // http://utvpn.tsukuba.ac.jp/\r
9 // \r
10 // This program is free software; you can redistribute it and/or\r
11 // modify it under the terms of the GNU General Public License\r
12 // version 2 as published by the Free Software Foundation.\r
13 // \r
14 // This program is distributed in the hope that it will be useful,\r
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of\r
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
17 // GNU General Public License for more details.\r
18 // \r
19 // You should have received a copy of the GNU General Public License version 2\r
20 // along with this program; if not, write to the Free Software\r
21 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.\r
22 // \r
23 // このファイルは GPL バージョン 2 ライセンスで公開されています。\r
24 // 誰でもこのファイルの内容を複製、改変したり、改変したバージョンを再配布\r
25 // することができます。ただし、原著作物を改変した場合は、原著作物の著作権表示\r
26 // を除去することはできません。改変した著作物を配布する場合は、改変実施者の\r
27 // 著作権表示を原著作物の著作権表示に付随して記載するようにしてください。\r
28 // \r
29 // この SoftEther UT-VPN オープンソース・プロジェクトは、日本国の\r
30 // ソフトイーサ株式会社 (SoftEther Corporation, http://www.softether.co.jp/ )\r
31 // および筑波大学 (University of Tsukuba, http://www.tsukuba.ac.jp/ ) によって\r
32 // ホストされています。\r
33 // 本プログラムの配布者は、本プログラムを、業としての利用以外のため、\r
34 // および、試験または研究のために利用が行われることを想定して配布\r
35 // しています。\r
36 // SoftEther UT-VPN プロジェクトの Web サイトは http://utvpn.tsukuba.ac.jp/ に\r
37 // あります。\r
38 // 本ソフトウェアの不具合の修正、機能改良、セキュリティホールの修復などのコード\r
39 // の改変を行った場合で、その成果物を SoftEther UT-VPN プロジェクトに提出して\r
40 // いただける場合は、 http://utvpn.tsukuba.ac.jp/ までソースコードを送付して\r
41 // ください。SoftEther UT-VPN プロジェクトの本体リリースまたはブランチリリース\r
42 // に組み込みさせていただきます。\r
43 // \r
44 // GPL に基づいて原著作物が提供される本ソフトウェアの改良版を配布、販売する\r
45 // 場合は、そのソースコードを GPL に基づいて誰にでも開示する義務が生じます。\r
46 // \r
47 // 本ソフトウェアに関連する著作権、特許権、商標権はソフトイーサ株式会社\r
48 // (SoftEther Corporation) およびその他の著作権保持者が保有しています。\r
49 // ソフトイーサ株式会社等はこれらの権利を放棄していません。本ソフトウェアの\r
50 // 二次著作物を配布、販売する場合は、これらの権利を侵害しないようにご注意\r
51 // ください。\r
52 // \r
53 // お願い: どのような通信ソフトウェアにも通常は必ず未発見の\r
54 // セキュリティホールが潜んでいます。本ソースコードをご覧いただいた結果、\r
55 // UT-VPN にセキュリティホールを発見された場合は、当該セキュリティホールの\r
56 // 情報を不特定多数に開示される前に、必ず、ソフトイーサ株式会社\r
57 // および脆弱性情報の届出を受け付ける公的機関まで通報いただき、\r
58 // 公益保護にご協力いただきますようお願い申し上げます。\r
59 // \r
60 // ソフトイーサ株式会社は、当該セキュリティホールについて迅速に対処を\r
61 // 行い、UT-VPN および UT-VPN に関連するソフトウェアのユーザー・顧客\r
62 // を保護するための努力を行います。\r
63 // \r
64 // ソフトイーサへの届出先: http://www.softether.co.jp/jp/contact/\r
65 // 日本国内の脆弱性情報届出受付公的機関:\r
66 //         独立行政法人 情報処理推進機構\r
67 //         http://www.ipa.go.jp/security/vuln/report/\r
68 // \r
69 // 上記各事項について不明な点は、ソフトイーサ株式会社までご連絡ください。\r
70 // 連絡先: http://www.softether.co.jp/jp/contact/\r
71 \r
72 // -----------------------------------------------\r
73 // [ChangeLog]\r
74 // 2010.05.20\r
75 //  新規リリース by SoftEther\r
76 // -----------------------------------------------\r
77 \r
78 // Logging.c\r
79 // ログ保存モジュール\r
80 \r
81 #include "CedarPch.h"\r
82 \r
83 static char *delete_targets[] =\r
84 {\r
85         "backup.vpn_bridge.config",\r
86         "backup.vpn_client.config",\r
87         "backup.vpn_server.config",\r
88         "backup.etherlogger.config",\r
89         "packet_log",\r
90         "etherlogger_log",\r
91         "secure_nat_log",\r
92         "security_log",\r
93         "server_log",\r
94         "bridge_log",\r
95 };\r
96 \r
97 // syslog の送信\r
98 void SendSysLog(SLOG *g, wchar_t *str)\r
99 {\r
100         UCHAR *buf;\r
101         UINT buf_size;\r
102         // 引数チェック\r
103         if (g == NULL || str == NULL)\r
104         {\r
105                 return;\r
106         }\r
107 \r
108         buf_size = CalcUniToUtf8(str);\r
109         buf = ZeroMalloc(buf_size);\r
110         UniToUtf8(buf, buf_size, str);\r
111 \r
112         if (buf_size >= 1024)\r
113         {\r
114                 buf_size = 1023;\r
115         }\r
116 \r
117         Lock(g->lock);\r
118         {\r
119                 if (Tick64() >= g->NextPollIp)\r
120                 {\r
121                         IP ip;\r
122 \r
123                         if (GetIP(&ip, g->HostName))\r
124                         {\r
125                                 g->NextPollIp = Tick64() + SYSLOG_POLL_IP_INTERVAL;\r
126                                 Copy(&g->DestIp, &ip, sizeof(IP));\r
127                         }\r
128                         else\r
129                         {\r
130                                 g->NextPollIp = Tick64() + SYSLOG_POLL_IP_INTERVAL_NG;\r
131                         }\r
132                 }\r
133 \r
134                 if (g->DestPort != 0 && IsZeroIp(&g->DestIp) == false)\r
135                 {\r
136                         SendTo(g->Udp, &g->DestIp, g->DestPort, buf, buf_size);\r
137                 }\r
138         }\r
139         Unlock(g->lock);\r
140 \r
141         Free(buf);\r
142 }\r
143 \r
144 // syslog クライアントの解放\r
145 void FreeSysLog(SLOG *g)\r
146 {\r
147         // 引数チェック\r
148         if (g == NULL)\r
149         {\r
150                 return;\r
151         }\r
152 \r
153         DeleteLock(g->lock);\r
154         ReleaseSock(g->Udp);\r
155         Free(g);\r
156 }\r
157 \r
158 // syslog クライアントの設定\r
159 void SetSysLog(SLOG *g, char *hostname, UINT port)\r
160 {\r
161         IP ip;\r
162         // 引数チェック\r
163         if (g == NULL)\r
164         {\r
165                 return;\r
166         }\r
167         if (port == 0)\r
168         {\r
169                 port = SYSLOG_PORT;\r
170         }\r
171 \r
172         if (hostname == NULL)\r
173         {\r
174                 hostname = "";\r
175         }\r
176 \r
177         Zero(&ip, sizeof(IP));\r
178         GetIP(&ip, hostname);\r
179 \r
180         Lock(g->lock);\r
181         {\r
182                 Copy(&g->DestIp, &ip, sizeof(IP));\r
183                 g->DestPort = port;\r
184                 StrCpy(g->HostName, sizeof(g->HostName), hostname);\r
185                 g->NextPollIp = Tick64() + IsZeroIp(&ip) ? SYSLOG_POLL_IP_INTERVAL_NG : SYSLOG_POLL_IP_INTERVAL;\r
186         }\r
187         Unlock(g->lock);\r
188 }\r
189 \r
190 // syslog クライアントの作成\r
191 SLOG *NewSysLog(char *hostname, UINT port)\r
192 {\r
193         // 引数チェック\r
194         SLOG *g = ZeroMalloc(sizeof(SLOG));\r
195 \r
196         g->lock = NewLock();\r
197         g->Udp = NewUDP(0);\r
198 \r
199         SetSysLog(g, hostname, port);\r
200 \r
201         return g;\r
202 }\r
203 \r
204 // ディスクに十分な空き容量があるかチェックする\r
205 bool CheckEraserDiskFreeSpace(ERASER *e)\r
206 {\r
207         UINT64 s;\r
208         // 引数チェック\r
209         if (e == NULL)\r
210         {\r
211                 return true;\r
212         }\r
213 \r
214         // ディスクの空き容量を取得する\r
215         if (GetDiskFree(e->DirName, &s, NULL, NULL) == false)\r
216         {\r
217                 // 取得失敗\r
218                 return true;\r
219         }\r
220 \r
221         if (e->MinFreeSpace > s)\r
222         {\r
223                 // 空き容量が指定されたバイト数未満である\r
224                 return false;\r
225         }\r
226 \r
227         // 十分空いている\r
228         return true;\r
229 }\r
230 \r
231 // 削除対象ファイルリストを解放する\r
232 void FreeEraseFileList(LIST *o)\r
233 {\r
234         UINT i;\r
235         // 引数チェック\r
236         if (o == NULL)\r
237         {\r
238                 return;\r
239         }\r
240 \r
241         for (i = 0;i < LIST_NUM(o);i++)\r
242         {\r
243                 ERASE_FILE *f = LIST_DATA(o, i);\r
244                 Free(f->FullPath);\r
245                 Free(f);\r
246         }\r
247 \r
248         ReleaseList(o);\r
249 }\r
250 \r
251 // 削除対象ファイルリストを表示する\r
252 void PrintEraseFileList(LIST *o)\r
253 {\r
254         UINT i;\r
255         // 引数チェック\r
256         if (o == NULL)\r
257         {\r
258                 return;\r
259         }\r
260 \r
261         for (i = 0;i < LIST_NUM(o);i++)\r
262         {\r
263                 ERASE_FILE *f = LIST_DATA(o, i);\r
264                 Print("%I64u - %s\n", f->UpdateTime, f->FullPath);\r
265         }\r
266 }\r
267 \r
268 // 指定されたディレクトリの削除対象ファイルリストを生成する\r
269 void EnumEraseFile(LIST *o, char *dirname)\r
270 {\r
271         DIRLIST *dir;\r
272         UINT i;\r
273         char tmp[MAX_PATH];\r
274         // 引数チェック\r
275         if (o == NULL || dirname == NULL)\r
276         {\r
277                 return;\r
278         }\r
279 \r
280         // 列挙\r
281         dir = EnumDir(dirname);\r
282 \r
283         for (i = 0;i < dir->NumFiles;i++)\r
284         {\r
285                 DIRENT *e = dir->File[i];\r
286                 Format(tmp, sizeof(tmp), "%s/%s", dirname, e->FileName);\r
287                 NormalizePath(tmp, sizeof(tmp), tmp);\r
288 \r
289                 if (e->Folder == false)\r
290                 {\r
291                         // ファイル\r
292                         ERASE_FILE *f;\r
293 \r
294                         if (EndWith(tmp, ".log") || EndWith(tmp, ".config"))\r
295                         {\r
296                                 // ログファイルと .config ファイルのみを対象とする\r
297                                 f = ZeroMalloc(sizeof(ERASE_FILE));\r
298                                 f->FullPath = CopyStr(tmp);\r
299                                 f->UpdateTime = e->UpdateDate;\r
300 \r
301                                 Add(o, f);\r
302                         }\r
303                 }\r
304                 else\r
305                 {\r
306                         // フォルダ\r
307                         EnumEraseFile(o, tmp);\r
308                 }\r
309         }\r
310 \r
311         FreeDir(dir);\r
312 }\r
313 \r
314 // 削除対象ファイルリストを生成する\r
315 LIST *GenerateEraseFileList(ERASER *e)\r
316 {\r
317         LIST *o;\r
318         UINT i;\r
319         // 引数チェック\r
320         if (e == NULL)\r
321         {\r
322                 return NULL;\r
323         }\r
324 \r
325         o = NewListFast(CompareEraseFile);\r
326 \r
327         // 各ディレクトリを走査する\r
328         for (i = 0;i < sizeof(delete_targets) / sizeof(delete_targets[0]);i++)\r
329         {\r
330                 char dirname[MAX_PATH];\r
331                 Format(dirname, sizeof(dirname), "%s/%s", e->DirName, delete_targets[i]);\r
332 \r
333                 EnumEraseFile(o, dirname);\r
334         }\r
335 \r
336         // ソートする\r
337         Sort(o);\r
338 \r
339         return o;\r
340 }\r
341 \r
342 // 不要ファイルの消去処理\r
343 void EraserMain(ERASER *e)\r
344 {\r
345         LIST *o;\r
346         UINT i;\r
347         bool ok = false;\r
348         char bs[64];\r
349         // 引数チェック\r
350         if (e == NULL)\r
351         {\r
352                 return;\r
353         }\r
354 \r
355         // まず空き容量をチェック\r
356         if (CheckEraserDiskFreeSpace(e))\r
357         {\r
358                 // 十分空いている\r
359                 return;\r
360         }\r
361 \r
362         ToStrByte(bs, sizeof(bs), e->MinFreeSpace);\r
363 \r
364         // ファイル一覧の生成\r
365         o = GenerateEraseFileList(e);\r
366 \r
367         // ファイルを古い順に 1 つずつ削除してみる\r
368         for (i = 0;i < LIST_NUM(o);i++)\r
369         {\r
370                 ERASE_FILE *f = LIST_DATA(o, i);\r
371 \r
372                 // ファイルを削除する\r
373                 if (FileDelete(f->FullPath))\r
374                 {\r
375                         ELog(e, "LE_DELETE", bs, f->FullPath);\r
376                 }\r
377 \r
378                 // 削除したあと空き容量を確認してみる\r
379                 if (CheckEraserDiskFreeSpace(e))\r
380                 {\r
381                         // 空き容量が回復した\r
382                         ok = true;\r
383                         break;\r
384                 }\r
385         }\r
386 \r
387         // ファイル一覧の解放\r
388         FreeEraseFileList(o);\r
389 \r
390         if (e->LastFailed == false && ok == false)\r
391         {\r
392                 // 空き容量が足りないがこれ以上ファイルを削除できない状態になってしまった\r
393                 ELog(e, "LE_NOT_ENOUGH_FREE", bs);\r
394         }\r
395 \r
396         e->LastFailed = ok ? false : true;\r
397 }\r
398 \r
399 // 削除するファイル項目の比較\r
400 int CompareEraseFile(void *p1, void *p2)\r
401 {\r
402         ERASE_FILE *f1, *f2;\r
403         if (p1 == NULL || p2 == NULL)\r
404         {\r
405                 return 0;\r
406         }\r
407         f1 = *(ERASE_FILE **)p1;\r
408         f2 = *(ERASE_FILE **)p2;\r
409         if (f1 == NULL || f2 == NULL)\r
410         {\r
411                 return 0;\r
412         }\r
413         if (f1->UpdateTime > f2->UpdateTime)\r
414         {\r
415                 return 1;\r
416         }\r
417         else if (f1->UpdateTime == f2->UpdateTime)\r
418         {\r
419                 return 0;\r
420         }\r
421         else\r
422         {\r
423                 return -1;\r
424         }\r
425 }\r
426 \r
427 // 自動ファイル削除器スレッド\r
428 void EraserThread(THREAD *t, void *p)\r
429 {\r
430         ERASER *e = (ERASER *)p;\r
431         char bs[64];\r
432         // 引数チェック\r
433         if (t == NULL || e == NULL)\r
434         {\r
435                 return;\r
436         }\r
437 \r
438         // 監視を開始\r
439         ToStrByte(bs, sizeof(bs), e->MinFreeSpace);\r
440         ELog(e, "LE_START", e->DirName, bs);\r
441 \r
442         while (e->Halt == false)\r
443         {\r
444                 // 一定間隔ごとにディスクの空き容量をチェックする\r
445                 EraserMain(e);\r
446 \r
447                 Wait(e->HaltEvent, DISK_FREE_CHECK_INTERVAL);\r
448         }\r
449 }\r
450 \r
451 // 新しい自動ファイル削除器の作成\r
452 ERASER *NewEraser(LOG *log, UINT64 min_size)\r
453 {\r
454         ERASER *e;\r
455         char dir[MAX_PATH];\r
456 \r
457         if (min_size == 0)\r
458         {\r
459                 min_size = DISK_FREE_SPACE_DEFAULT;\r
460         }\r
461 \r
462         if (min_size < DISK_FREE_SPACE_MIN)\r
463         {\r
464                 min_size = DISK_FREE_SPACE_MIN;\r
465         }\r
466 \r
467         e = ZeroMalloc(sizeof(ERASER));\r
468 \r
469         GetExeDir(dir, sizeof(dir));\r
470 \r
471         e->Log = log;\r
472         e->MinFreeSpace = min_size;\r
473         e->DirName = CopyStr(dir);\r
474         e->HaltEvent = NewEvent();\r
475 \r
476         e->Thread = NewThread(EraserThread, e);\r
477 \r
478         return e;\r
479 }\r
480 \r
481 // 自動ファイル削除器の解放\r
482 void FreeEraser(ERASER *e)\r
483 {\r
484         // 引数チェック\r
485         if (e == NULL)\r
486         {\r
487                 return;\r
488         }\r
489 \r
490         e->Halt = true;\r
491         Set(e->HaltEvent);\r
492         WaitThread(e->Thread, INFINITE);\r
493         ReleaseThread(e->Thread);\r
494         ReleaseEvent(e->HaltEvent);\r
495 \r
496         Free(e->DirName);\r
497         Free(e);\r
498 }\r
499 \r
500 // デバッグログをとる (可変長引数)\r
501 void DebugLog(CEDAR *c, char *fmt, ...)\r
502 {\r
503         char buf[MAX_SIZE * 2];\r
504         va_list args;\r
505         // 引数チェック\r
506         if (fmt == NULL)\r
507         {\r
508                 return;\r
509         }\r
510         if (c->DebugLog == NULL)\r
511         {\r
512                 return;\r
513         }\r
514 \r
515         va_start(args, fmt);\r
516         FormatArgs(buf, sizeof(buf), fmt, args);\r
517 \r
518         InsertStringRecord(c->DebugLog, buf);\r
519         va_end(args);\r
520 }\r
521 \r
522 // 自動ファイル削除器のログをとる\r
523 void ELog(ERASER *e, char *name, ...)\r
524 {\r
525         wchar_t buf[MAX_SIZE * 2];\r
526         va_list args;\r
527         // 引数チェック\r
528         if (name == NULL)\r
529         {\r
530                 return;\r
531         }\r
532 \r
533         va_start(args, name);\r
534         UniFormatArgs(buf, sizeof(buf), _UU(name), args);\r
535 \r
536         InsertUnicodeRecord(e->Log, buf);\r
537 \r
538         if (IsDebug())\r
539         {\r
540                 UniPrint(L"LOG: %s\n", buf);\r
541         }\r
542         va_end(args);\r
543 }\r
544 \r
545 // サーバーのログをとる\r
546 void ServerLog(CEDAR *c, wchar_t *fmt, ...)\r
547 {\r
548         wchar_t buf[MAX_SIZE * 2];\r
549         va_list args;\r
550         // 引数チェック\r
551         if (fmt == NULL)\r
552         {\r
553                 return;\r
554         }\r
555 \r
556         va_start(args, fmt);\r
557         UniFormatArgs(buf, sizeof(buf), fmt, args);\r
558 \r
559         WriteServerLog(c, buf);\r
560         va_end(args);\r
561 }\r
562 void SLog(CEDAR *c, char *name, ...)\r
563 {\r
564         wchar_t buf[MAX_SIZE * 2];\r
565         va_list args;\r
566         // 引数チェック\r
567         if (name == NULL)\r
568         {\r
569                 return;\r
570         }\r
571 \r
572         va_start(args, name);\r
573         UniFormatArgs(buf, sizeof(buf), _UU(name), args);\r
574 \r
575         WriteServerLog(c, buf);\r
576         va_end(args);\r
577 }\r
578 \r
579 // クライアントログ\r
580 void CLog(CLIENT *c, char *name, ...)\r
581 {\r
582         wchar_t buf[MAX_SIZE * 2];\r
583         va_list args;\r
584         // 引数チェック\r
585         if (name == NULL)\r
586         {\r
587                 return;\r
588         }\r
589 \r
590         if (c == NULL || c->NoSaveLog)\r
591         {\r
592                 return;\r
593         }\r
594 \r
595         va_start(args, name);\r
596         UniFormatArgs(buf, sizeof(buf), _UU(name), args);\r
597 \r
598         WriteClientLog(c, buf);\r
599         va_end(args);\r
600 }\r
601 \r
602 // HUB のセキュリティログをとる\r
603 void HubLog(HUB *h, wchar_t *fmt, ...)\r
604 {\r
605         wchar_t buf[MAX_SIZE * 2];\r
606         va_list args;\r
607         // 引数チェック\r
608         if (fmt == NULL)\r
609         {\r
610                 return;\r
611         }\r
612 \r
613         va_start(args, fmt);\r
614         UniFormatArgs(buf, sizeof(buf), fmt, args);\r
615 \r
616         WriteHubLog(h, buf);\r
617         va_end(args);\r
618 }\r
619 void ALog(ADMIN *a, HUB *h, char *name, ...)\r
620 {\r
621         wchar_t buf[MAX_SIZE * 2];\r
622         wchar_t tmp[MAX_SIZE * 2];\r
623         va_list args;\r
624         RPC *r;\r
625         // 引数チェック\r
626         if (a == NULL || name == NULL)\r
627         {\r
628                 return;\r
629         }\r
630 \r
631         r = a->Rpc;\r
632 \r
633         va_start(args, name);\r
634         UniFormatArgs(buf, sizeof(buf), _UU(name), args);\r
635 \r
636         if (h == NULL)\r
637         {\r
638                 UniFormat(tmp, sizeof(tmp), _UU("LA_TAG_1"), r->Name);\r
639         }\r
640         else\r
641         {\r
642                 UniFormat(tmp, sizeof(tmp), _UU("LA_TAG_2"), r->Name, h->Name);\r
643         }\r
644 \r
645         UniStrCat(tmp, sizeof(tmp), buf);\r
646 \r
647         if (h == NULL)\r
648         {\r
649                 WriteServerLog(((ADMIN *)r->Param)->Server->Cedar, tmp);\r
650         }\r
651         else\r
652         {\r
653                 WriteHubLog(h, tmp);\r
654         }\r
655         va_end(args);\r
656 }\r
657 void HLog(HUB *h, char *name, ...)\r
658 {\r
659         wchar_t buf[MAX_SIZE * 2];\r
660         va_list args;\r
661         // 引数チェック\r
662         if (name == NULL)\r
663         {\r
664                 return;\r
665         }\r
666 \r
667         va_start(args, name);\r
668         UniFormatArgs(buf, sizeof(buf), _UU(name), args);\r
669 \r
670         WriteHubLog(h, buf);\r
671         va_end(args);\r
672 }\r
673 void NLog(VH *v, char *name, ...)\r
674 {\r
675         wchar_t buf[MAX_SIZE * 2];\r
676         static wchar_t snat_prefix[] = L"SecureNAT: ";\r
677         va_list args;\r
678         // 引数チェック\r
679         if (name == NULL || v == NULL || v->nat == NULL || v->nat->SecureNAT == NULL)\r
680         {\r
681                 return;\r
682         }\r
683 \r
684         va_start(args, name);\r
685         Copy(buf, snat_prefix, sizeof(snat_prefix));\r
686         UniFormatArgs(&buf[11], sizeof(buf) - 12 * sizeof(wchar_t), _UU(name), args);\r
687 \r
688         WriteHubLog(v->nat->SecureNAT->Hub, buf);\r
689         va_end(args);\r
690 }\r
691 \r
692 // HUB のセキュリティログを保存する\r
693 void WriteHubLog(HUB *h, wchar_t *str)\r
694 {\r
695         wchar_t buf[MAX_SIZE * 2];\r
696         SERVER *s;\r
697         // 引数チェック\r
698         if (h == NULL || str == NULL)\r
699         {\r
700                 return;\r
701         }\r
702 \r
703         s = h->Cedar->Server;\r
704 \r
705         UniFormat(buf, sizeof(buf), L"[HUB \"%S\"] %s", h->Name, str);\r
706 \r
707         WriteServerLog(h->Cedar, buf);\r
708 \r
709         if (h->LogSetting.SaveSecurityLog == false)\r
710         {\r
711                 return;\r
712         }\r
713 \r
714         InsertUnicodeRecord(h->SecurityLogger, str);\r
715 }\r
716 \r
717 // クライアントログを保存する\r
718 void WriteClientLog(CLIENT *c, wchar_t *str)\r
719 {\r
720         // 引数チェック\r
721         if (c == NULL)\r
722         {\r
723                 return;\r
724         }\r
725 \r
726         InsertUnicodeRecord(c->Logger, str);\r
727 }\r
728 \r
729 // サーバーのセキュリティログを保存する\r
730 void WriteServerLog(CEDAR *c, wchar_t *str)\r
731 {\r
732         SERVER *s;\r
733         // 引数チェック\r
734         if (c == NULL || str == NULL)\r
735         {\r
736                 return;\r
737         }\r
738 \r
739         s = c->Server;\r
740         if (s == NULL)\r
741         {\r
742                 return;\r
743         }\r
744 \r
745         if (IsDebug())\r
746         {\r
747                 UniPrint(L"LOG: %s\n", str);\r
748         }\r
749 \r
750         InsertUnicodeRecord(s->Logger, str);\r
751 }\r
752 \r
753 // 複数行にわたるログを書き出す\r
754 void WriteMultiLineLog(LOG *g, BUF *b)\r
755 {\r
756         // 引数チェック\r
757         if (g == NULL || b == NULL)\r
758         {\r
759                 return;\r
760         }\r
761 \r
762         SeekBuf(b, 0, 0);\r
763 \r
764         while (true)\r
765         {\r
766                 char *s = CfgReadNextLine(b);\r
767                 if (s == NULL)\r
768                 {\r
769                         break;\r
770                 }\r
771 \r
772                 if (IsEmptyStr(s) == false)\r
773                 {\r
774                         InsertStringRecord(g, s);\r
775                 }\r
776 \r
777                 Free(s);\r
778         }\r
779 }\r
780 \r
781 // セキュリティログをとる (可変長引数) ※ 廃止\r
782 void SecLog(HUB *h, char *fmt, ...)\r
783 {\r
784         char buf[MAX_SIZE * 2];\r
785         va_list args;\r
786         // 引数チェック\r
787         if (fmt == NULL)\r
788         {\r
789                 return;\r
790         }\r
791 \r
792         if (h->LogSetting.SaveSecurityLog == false)\r
793         {\r
794                 return;\r
795         }\r
796 \r
797         va_start(args, fmt);\r
798         FormatArgs(buf, sizeof(buf), fmt, args);\r
799 \r
800         WriteSecurityLog(h, buf);\r
801         va_end(args);\r
802 }\r
803 \r
804 // セキュリティログをとる\r
805 void WriteSecurityLog(HUB *h, char *str)\r
806 {\r
807         // 引数チェック\r
808         if (h == NULL || str == NULL)\r
809         {\r
810                 return;\r
811         }\r
812 \r
813         InsertStringRecord(h->SecurityLogger, str);\r
814 }\r
815 \r
816 // パケットログをとる\r
817 void PacketLog(HUB *hub, SESSION *src_session, SESSION *dest_session, PKT *packet)\r
818 {\r
819         UINT level;\r
820         PKT *p;\r
821         PACKET_LOG *pl;\r
822         SERVER *s;\r
823         bool no_log = false;\r
824         // 引数チェック\r
825         if (hub == NULL || src_session == NULL || packet == NULL)\r
826         {\r
827                 return;\r
828         }\r
829 \r
830         s = hub->Cedar->Server;\r
831 \r
832         if (hub->LogSetting.SavePacketLog == false)\r
833         {\r
834                 // パケットログをとらない\r
835                 return;\r
836         }\r
837 \r
838         if (Cmp(hub->HubMacAddr, packet->MacAddressSrc, 6) == 0 ||\r
839                 Cmp(hub->HubMacAddr, packet->MacAddressDest, 6) == 0)\r
840         {\r
841                 return;\r
842         }\r
843 \r
844         // ロギングレベルの決定\r
845         level = CalcPacketLoggingLevel(hub, packet);\r
846         if (level == PACKET_LOG_NONE)\r
847         {\r
848                 // 保存しない\r
849                 return;\r
850         }\r
851 \r
852         if (hub->Option != NULL)\r
853         {\r
854                 if (hub->Option->NoIPv4PacketLog && (packet->TypeL3 == L3_IPV4 || packet->TypeL3 == L3_ARPV4))\r
855                 {\r
856                         // IPv4 パケットログを一切保存しない\r
857                         return;\r
858                 }\r
859 \r
860                 if (hub->Option->NoIPv6PacketLog && packet->TypeL3 == L3_IPV6)\r
861                 {\r
862                         // IPv6 パケットログを一切保存しない\r
863                         return;\r
864                 }\r
865         }\r
866 \r
867         if (s->Cedar->Bridge == false)\r
868         {\r
869                 if (s->LicenseSystem != NULL && s->LicenseSystem->Status != NULL)\r
870                 {\r
871                         if (s->LicenseSystem->Status->AllowEnterpriseFunction == false &&\r
872                                 s->LicenseSystem->Status->Edition != 0)\r
873                         {\r
874                                 // VPN Server の製品エディションが低い場合はパケットログのうち一部\r
875                                 // を保存しない\r
876                                 no_log = true;\r
877                         }\r
878                 }\r
879         }\r
880 \r
881         // パケットのクローン\r
882         p = ClonePacket(packet, level == PACKET_LOG_ALL ? true : false);\r
883 \r
884         // 情報の取得\r
885         pl = ZeroMalloc(sizeof(PACKET_LOG));\r
886         pl->Cedar = hub->Cedar;\r
887         pl->Packet = p;\r
888         pl->NoLog = no_log;\r
889         if (src_session != NULL)\r
890         {\r
891                 pl->SrcSessionName = CopyStr(src_session->Name);\r
892         }\r
893         else\r
894         {\r
895                 pl->SrcSessionName = CopyStr("");\r
896         }\r
897         if (dest_session != NULL)\r
898         {\r
899                 pl->DestSessionName = CopyStr(dest_session->Name);\r
900         }\r
901         else\r
902         {\r
903                 pl->DestSessionName = CopyStr("");\r
904         }\r
905 \r
906         if (src_session->LoggingRecordCount != NULL)\r
907         {\r
908                 UINT n = 0;\r
909                 while (src_session->LoggingRecordCount->c >= 30000)\r
910                 {\r
911                         SleepThread(50);\r
912                         n++;\r
913                         if (n >= 5)\r
914                         {\r
915                                 break;\r
916                         }\r
917                 }\r
918         }\r
919 \r
920         pl->SrcSession = src_session;\r
921         AddRef(src_session->ref);\r
922 \r
923         Inc(src_session->LoggingRecordCount);\r
924 \r
925         // パケットログの挿入\r
926         InsertRecord(hub->PacketLogger, pl, PacketLogParseProc);\r
927 }\r
928 \r
929 // 指定されたパケットのロギングレベルを計算する\r
930 UINT CalcPacketLoggingLevelEx(HUB_LOG *g, PKT *packet)\r
931 {\r
932         UINT ret = 0;\r
933         // 引数チェック\r
934         if (g == NULL || packet == NULL)\r
935         {\r
936                 return PACKET_LOG_NONE;\r
937         }\r
938 \r
939         // Ethernet ログ\r
940         ret = MAX(ret, g->PacketLogConfig[PACKET_LOG_ETHERNET]);\r
941 \r
942         switch (packet->TypeL3)\r
943         {\r
944         case L3_ARPV4:\r
945                 // ARP\r
946                 ret = MAX(ret, g->PacketLogConfig[PACKET_LOG_ARP]);\r
947                 break;\r
948 \r
949         case L3_IPV4:\r
950                 // IPv4\r
951                 ret = MAX(ret, g->PacketLogConfig[PACKET_LOG_IP]);\r
952 \r
953                 switch (packet->TypeL4)\r
954                 {\r
955                 case L4_ICMPV4:\r
956                         // ICMPv4\r
957                         ret = MAX(ret, g->PacketLogConfig[PACKET_LOG_ICMP]);\r
958                         break;\r
959 \r
960                 case L4_TCP:\r
961                         // TCPv4\r
962                         ret = MAX(ret, g->PacketLogConfig[PACKET_LOG_TCP]);\r
963 \r
964                         if (packet->L4.TCPHeader->Flag & TCP_SYN ||\r
965                                 packet->L4.TCPHeader->Flag & TCP_RST ||\r
966                                 packet->L4.TCPHeader->Flag & TCP_FIN)\r
967                         {\r
968                                 // TCP SYN LOG\r
969                                 ret = MAX(ret, g->PacketLogConfig[PACKET_LOG_TCP_CONN]);\r
970                         }\r
971 \r
972                         break;\r
973 \r
974                 case L4_UDP:\r
975                         // UDPv4\r
976                         ret = MAX(ret, g->PacketLogConfig[PACKET_LOG_UDP]);\r
977 \r
978                         switch (packet->TypeL7)\r
979                         {\r
980                         case L7_DHCPV4:\r
981                                 // DHCPv4\r
982                                 ret = MAX(ret, g->PacketLogConfig[PACKET_LOG_DHCP]);\r
983                                 break;\r
984                         }\r
985 \r
986                         break;\r
987                 }\r
988 \r
989                 break;\r
990 \r
991         case L3_IPV6:\r
992                 // IPv6\r
993                 ret = MAX(ret, g->PacketLogConfig[PACKET_LOG_IP]);\r
994 \r
995                 switch (packet->TypeL4)\r
996                 {\r
997                 case L4_ICMPV6:\r
998                         // ICMPv6\r
999                         ret = MAX(ret, g->PacketLogConfig[PACKET_LOG_ICMP]);\r
1000                         break;\r
1001 \r
1002                 case L4_TCP:\r
1003                         // TCPv6\r
1004                         ret = MAX(ret, g->PacketLogConfig[PACKET_LOG_TCP]);\r
1005 \r
1006                         if (packet->L4.TCPHeader->Flag & TCP_SYN ||\r
1007                                 packet->L4.TCPHeader->Flag & TCP_RST ||\r
1008                                 packet->L4.TCPHeader->Flag & TCP_FIN)\r
1009                         {\r
1010                                 // TCP SYN LOG\r
1011                                 ret = MAX(ret, g->PacketLogConfig[PACKET_LOG_TCP_CONN]);\r
1012                         }\r
1013 \r
1014                         break;\r
1015 \r
1016                 case L4_UDP:\r
1017                         // UDPv6\r
1018                         ret = MAX(ret, g->PacketLogConfig[PACKET_LOG_UDP]);\r
1019 \r
1020                         break;\r
1021                 }\r
1022 \r
1023                 break;\r
1024         }\r
1025 \r
1026         return ret;\r
1027 }\r
1028 UINT CalcPacketLoggingLevel(HUB *hub, PKT *packet)\r
1029 {\r
1030         // 引数チェック\r
1031         if (hub == NULL || packet == NULL)\r
1032         {\r
1033                 return PACKET_LOG_NONE;\r
1034         }\r
1035 \r
1036         return CalcPacketLoggingLevelEx(&hub->LogSetting, packet);\r
1037 }\r
1038 \r
1039 // パケットログエントリを文字列に変換するプロシージャ\r
1040 char *PacketLogParseProc(RECORD *rec)\r
1041 {\r
1042         PACKET_LOG *pl;\r
1043         PKT *p;\r
1044         char *s;\r
1045         TOKEN_LIST *t;\r
1046         char tmp[MAX_SIZE];\r
1047         // 引数チェック\r
1048         if (rec == NULL)\r
1049         {\r
1050                 return NULL;\r
1051         }\r
1052 \r
1053         pl = (PACKET_LOG *)rec->Data;\r
1054         p = pl->Packet;\r
1055 \r
1056         // 各部を生成する\r
1057         t = ZeroMalloc(sizeof(TOKEN_LIST));\r
1058         t->NumTokens = 16;\r
1059         t->Token = ZeroMalloc(sizeof(char *) * t->NumTokens);\r
1060 \r
1061         // 送信元セッション\r
1062         t->Token[0] = pl->SrcSessionName;\r
1063 \r
1064         // 宛先セッション\r
1065         t->Token[1] = pl->DestSessionName;\r
1066 \r
1067         // 送信元 MAC アドレス\r
1068         BinToStr(tmp, sizeof(tmp), p->MacAddressSrc, 6);\r
1069 \r
1070         t->Token[2] = CopyStr(tmp);\r
1071         // 宛先 MAC アドレス\r
1072         BinToStr(tmp, sizeof(tmp), p->MacAddressDest, 6);\r
1073 \r
1074         t->Token[3] = CopyStr(tmp);\r
1075 \r
1076         // MAC プロトコル\r
1077         snprintf(tmp, sizeof(tmp), "0x%04X", Endian16(p->MacHeader->Protocol));\r
1078         t->Token[4] = CopyStr(tmp);\r
1079 \r
1080         // パケットサイズ\r
1081         ToStr(tmp, p->PacketSize);\r
1082         t->Token[5] = CopyStr(tmp);\r
1083 \r
1084         // パケットログ本体は実装されていない\r
1085         t->Token[6] = CopyUniToUtf(_UU("LH_PACKET_LOG_NO_LOG"));\r
1086 \r
1087         s = GenCsvLine(t);\r
1088         FreeToken(t);\r
1089 \r
1090         // パケットデータを破棄する\r
1091         if (pl->PurePacket == false)\r
1092         {\r
1093                 FreeClonePacket(p);\r
1094         }\r
1095         else\r
1096         {\r
1097                 Free(p->PacketData);\r
1098                 FreePacket(p);\r
1099         }\r
1100 \r
1101         // セッションを解放する\r
1102         if (pl->SrcSession != NULL)\r
1103         {\r
1104                 Dec(pl->SrcSession->LoggingRecordCount);\r
1105                 ReleaseSession(pl->SrcSession);\r
1106         }\r
1107 \r
1108         // PACKET_LOG を破棄する\r
1109         Free(pl);\r
1110 \r
1111         return s;\r
1112 }\r
1113 \r
1114 // TCP フラグを文字列に変換\r
1115 char *TcpFlagStr(UCHAR flag)\r
1116 {\r
1117         char tmp[MAX_SIZE];\r
1118         StrCpy(tmp, sizeof(tmp), "");\r
1119 \r
1120         if (flag & TCP_FIN)\r
1121         {\r
1122                 StrCat(tmp, sizeof(tmp), "FIN+");\r
1123         }\r
1124 \r
1125         if (flag & TCP_SYN)\r
1126         {\r
1127                 StrCat(tmp, sizeof(tmp), "SYN+");\r
1128         }\r
1129 \r
1130         if (flag & TCP_RST)\r
1131         {\r
1132                 StrCat(tmp, sizeof(tmp), "RST+");\r
1133         }\r
1134 \r
1135         if (flag & TCP_PSH)\r
1136         {\r
1137                 StrCat(tmp, sizeof(tmp), "PSH+");\r
1138         }\r
1139 \r
1140         if (flag & TCP_ACK)\r
1141         {\r
1142                 StrCat(tmp, sizeof(tmp), "ACK+");\r
1143         }\r
1144 \r
1145         if (flag & TCP_URG)\r
1146         {\r
1147                 StrCat(tmp, sizeof(tmp), "URG+");\r
1148         }\r
1149 \r
1150         if (StrLen(tmp) >= 1)\r
1151         {\r
1152                 if (tmp[StrLen(tmp) - 1] == '+')\r
1153                 {\r
1154                         tmp[StrLen(tmp) - 1] = 0;\r
1155                 }\r
1156         }\r
1157 \r
1158         return CopyStr(tmp);\r
1159 }\r
1160 \r
1161 // ポート文字列の生成\r
1162 char *PortStr(CEDAR *cedar, UINT port, bool udp)\r
1163 {\r
1164         char tmp[MAX_SIZE];\r
1165         char *name;\r
1166         // 引数チェック\r
1167         if (cedar == NULL)\r
1168         {\r
1169                 return NULL;\r
1170         }\r
1171 \r
1172         name = GetSvcName(cedar, udp, port);\r
1173 \r
1174         if (name == NULL)\r
1175         {\r
1176                 snprintf(tmp, sizeof(tmp), "%u", port);\r
1177         }\r
1178         else\r
1179         {\r
1180                 snprintf(tmp, sizeof(tmp), "%s(%u)", name, port);\r
1181         }\r
1182 \r
1183         return CopyStr(tmp);\r
1184 }\r
1185 \r
1186 // カンマで区切られた文字列を生成する\r
1187 char *GenCsvLine(TOKEN_LIST *t)\r
1188 {\r
1189         UINT i;\r
1190         BUF *b;\r
1191         char *ret;\r
1192         // 引数チェック\r
1193         if (t == NULL)\r
1194         {\r
1195                 return NULL;\r
1196         }\r
1197 \r
1198         b = NewBuf();\r
1199         for (i = 0;i < t->NumTokens;i++)\r
1200         {\r
1201                 if (t->Token[i] != NULL)\r
1202                 {\r
1203                         ReplaceForCsv(t->Token[i]);\r
1204                         if (StrLen(t->Token[i]) == 0)\r
1205                         {\r
1206                                 WriteBuf(b, "-", 1);\r
1207                         }\r
1208                         else\r
1209                         {\r
1210                                 WriteBuf(b, t->Token[i], StrLen(t->Token[i]));\r
1211                         }\r
1212                 }\r
1213                 else\r
1214                 {\r
1215                         WriteBuf(b, "-", 1);\r
1216                 }\r
1217                 if (i != (t->NumTokens - 1))\r
1218                 {\r
1219                         WriteBuf(b, ",", 1);\r
1220                 }\r
1221         }\r
1222         WriteBuf(b, "\0", 1);\r
1223 \r
1224         ret = (char *)b->Buf;\r
1225 \r
1226         Free(b);\r
1227 \r
1228         return ret;\r
1229 }\r
1230 \r
1231 // CSV の中に入る文字列を正しく置換する\r
1232 void ReplaceForCsv(char *str)\r
1233 {\r
1234         UINT i, len;\r
1235         // 引数チェック\r
1236         if (str == NULL)\r
1237         {\r
1238                 return;\r
1239         }\r
1240 \r
1241         // 空白があればトリミングする\r
1242         Trim(str);\r
1243         len = StrLen(str);\r
1244 \r
1245         for (i = 0;i < len;i++)\r
1246         {\r
1247                 // カンマをアンダーバーに変換する\r
1248                 if (str[i] == ',')\r
1249                 {\r
1250                         str[i] = '_';\r
1251                 }\r
1252         }\r
1253 }\r
1254 \r
1255 // ログのディレクトリ名を設定\r
1256 void SetLogDirName(LOG *g, char *dir)\r
1257 {\r
1258         // 引数チェック\r
1259         if (g == NULL || dir == NULL)\r
1260         {\r
1261                 return;\r
1262         }\r
1263 \r
1264         LockLog(g);\r
1265         {\r
1266                 if (g->DirName != NULL)\r
1267                 {\r
1268                         Free(g->DirName);\r
1269                 }\r
1270                 g->DirName = CopyStr(dir);\r
1271         }\r
1272         UnlockLog(g);\r
1273 }\r
1274 \r
1275 // ログの名前を設定\r
1276 void SetLogPrefix(LOG *g, char *prefix)\r
1277 {\r
1278         // 引数チェック\r
1279         if (g == NULL || prefix == NULL)\r
1280         {\r
1281                 return;\r
1282         }\r
1283 \r
1284         LockLog(g);\r
1285         {\r
1286                 if (g->DirName != NULL)\r
1287                 {\r
1288                         Free(g->Prefix);\r
1289                 }\r
1290                 g->DirName = CopyStr(prefix);\r
1291         }\r
1292         UnlockLog(g);\r
1293 }\r
1294 \r
1295 // ログのスイッチ種類を設定\r
1296 void SetLogSwitchType(LOG *g, UINT switch_type)\r
1297 {\r
1298         // 引数チェック\r
1299         if (g == NULL)\r
1300         {\r
1301                 return;\r
1302         }\r
1303 \r
1304         LockLog(g);\r
1305         {\r
1306                 g->SwitchType = switch_type;\r
1307         }\r
1308         UnlockLog(g);\r
1309 }\r
1310 \r
1311 // 文字列レコードの解析\r
1312 char *StringRecordParseProc(RECORD *rec)\r
1313 {\r
1314         // 引数チェック\r
1315         if (rec == NULL)\r
1316         {\r
1317                 return NULL;\r
1318         }\r
1319 \r
1320         return (char *)rec->Data;\r
1321 }\r
1322 \r
1323 // ログに Unicode 文字列レコードを追加\r
1324 void InsertUnicodeRecord(LOG *g, wchar_t *unistr)\r
1325 {\r
1326         char *str;\r
1327         UINT size;\r
1328         // 引数チェック\r
1329         if (g == NULL || unistr == NULL)\r
1330         {\r
1331                 return;\r
1332         }\r
1333 \r
1334         size = CalcUniToUtf8(unistr) + 32;\r
1335         str = ZeroMalloc(size);\r
1336 \r
1337         UniToUtf8((BYTE *)str, size, unistr);\r
1338         InsertStringRecord(g, str);\r
1339         Free(str);\r
1340 }\r
1341 \r
1342 // ログに文字列レコードを追加\r
1343 void InsertStringRecord(LOG *g, char *str)\r
1344 {\r
1345         char *str_copy;\r
1346         // 引数チェック\r
1347         if (g == NULL || str == NULL)\r
1348         {\r
1349                 return;\r
1350         }\r
1351 \r
1352         str_copy = CopyStr(str);\r
1353 \r
1354         InsertRecord(g, str_copy, StringRecordParseProc);\r
1355 }\r
1356 \r
1357 // ログにレコードを追加\r
1358 void InsertRecord(LOG *g, void *data, RECORD_PARSE_PROC *proc)\r
1359 {\r
1360         RECORD *rec;\r
1361         // 引数チェック\r
1362         if (g == NULL || data == NULL || proc == NULL)\r
1363         {\r
1364                 return;\r
1365         }\r
1366 \r
1367         rec = ZeroMalloc(sizeof(RECORD));\r
1368         rec->Tick = Tick64();\r
1369         rec->ParseProc = proc;\r
1370         rec->Data = data;\r
1371 \r
1372         LockQueue(g->RecordQueue);\r
1373         {\r
1374                 InsertQueue(g->RecordQueue, rec);\r
1375         }\r
1376         UnlockQueue(g->RecordQueue);\r
1377 \r
1378         Set(g->Event);\r
1379 }\r
1380 \r
1381 // ログのロック\r
1382 void LockLog(LOG *g)\r
1383 {\r
1384         // 引数チェック\r
1385         if (g == NULL)\r
1386         {\r
1387                 return;\r
1388         }\r
1389 \r
1390         Lock(g->lock);\r
1391 }\r
1392 \r
1393 // ログのロック解除\r
1394 void UnlockLog(LOG *g)\r
1395 {\r
1396         // 引数チェック\r
1397         if (g == NULL)\r
1398         {\r
1399                 return;\r
1400         }\r
1401 \r
1402         Unlock(g->lock);\r
1403 }\r
1404 \r
1405 // ログファイル名の文字列部分を時刻とスイッチ規則から生成する\r
1406 void MakeLogFileNameStringFromTick(LOG *g, char *str, UINT size, UINT64 tick, UINT switch_type)\r
1407 {\r
1408         UINT64 time;\r
1409         SYSTEMTIME st;\r
1410 \r
1411         // 引数チェック\r
1412         if (str == NULL || g == NULL)\r
1413         {\r
1414                 return;\r
1415         }\r
1416 \r
1417         if (g->CacheFlag)\r
1418         {\r
1419                 if (g->LastTick == tick &&\r
1420                         g->LastSwitchType == switch_type)\r
1421                 {\r
1422                         StrCpy(str, size, g->LastStr);\r
1423                         return;\r
1424                 }\r
1425         }\r
1426 \r
1427         time = TickToTime(tick);\r
1428         UINT64ToSystem(&st, SystemToLocal64(time));\r
1429 \r
1430         switch (switch_type)\r
1431         {\r
1432         case LOG_SWITCH_SECOND: // 1 秒単位\r
1433                 snprintf(str, size, "_%04u%02u%02u_%02u%02u%02u",\r
1434                         st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond);\r
1435                 break;\r
1436 \r
1437         case LOG_SWITCH_MINUTE: // 1 分単位\r
1438                 snprintf(str, size, "_%04u%02u%02u_%02u%02u",\r
1439                         st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute);\r
1440                 break;\r
1441 \r
1442         case LOG_SWITCH_HOUR:   // 1 時間単位\r
1443                 snprintf(str, size, "_%04u%02u%02u_%02u", st.wYear, st.wMonth, st.wDay, st.wHour);\r
1444                 break;\r
1445 \r
1446         case LOG_SWITCH_DAY:    // 1 日単位\r
1447                 snprintf(str, size, "_%04u%02u%02u", st.wYear, st.wMonth, st.wDay);\r
1448                 break;\r
1449 \r
1450         case LOG_SWITCH_MONTH:  // 1 ヶ月単位\r
1451                 snprintf(str, size, "_%04u%02u", st.wYear, st.wMonth);\r
1452                 break;\r
1453 \r
1454         default:                                // 切り替え無し\r
1455                 snprintf(str, size, "");\r
1456                 break;\r
1457         }\r
1458 \r
1459         g->CacheFlag = true;\r
1460         g->LastTick = tick;\r
1461         g->LastSwitchType = switch_type;\r
1462         StrCpy(g->LastStr, sizeof(g->LastStr), str);\r
1463 }\r
1464 \r
1465 // ログファイル名を作成する\r
1466 bool MakeLogFileName(LOG *g, char *name, UINT size, char *dir, char *prefix, UINT64 tick, UINT switch_type, UINT num, char *old_datestr)\r
1467 {\r
1468         char tmp[MAX_SIZE];\r
1469         char tmp2[64];\r
1470         bool ret = false;\r
1471         // 引数チェック\r
1472         if (g == NULL || name == NULL || prefix == NULL || old_datestr == NULL)\r
1473         {\r
1474                 return false;\r
1475         }\r
1476 \r
1477         MakeLogFileNameStringFromTick(g, tmp, sizeof(tmp), tick, switch_type);\r
1478 \r
1479         if (num == 0)\r
1480         {\r
1481                 tmp2[0] = 0;\r
1482         }\r
1483         else\r
1484         {\r
1485                 snprintf(tmp2, sizeof(tmp2), "~%02u", num);\r
1486         }\r
1487 \r
1488         if (strcmp(old_datestr, tmp) != 0)\r
1489         {\r
1490                 ret = true;\r
1491                 strcpy(old_datestr, tmp);\r
1492         }\r
1493 \r
1494         snprintf(name, size, "%s%s%s%s%s.log", dir,\r
1495                 StrLen(dir) == 0 ? "" : "/",\r
1496                 prefix, tmp, tmp2\r
1497                 );\r
1498 \r
1499         return ret;\r
1500 }\r
1501 \r
1502 // ログがフラッシュされるまで待機\r
1503 void WaitLogFlush(LOG *g)\r
1504 {\r
1505         // 引数チェック\r
1506         if (g == NULL)\r
1507         {\r
1508                 return;\r
1509         }\r
1510 \r
1511         while (true)\r
1512         {\r
1513                 UINT num;\r
1514                 LockQueue(g->RecordQueue);\r
1515                 {\r
1516                         num = g->RecordQueue->num_item;\r
1517                 }\r
1518                 UnlockQueue(g->RecordQueue);\r
1519 \r
1520                 if (num == 0)\r
1521                 {\r
1522                         break;\r
1523                 }\r
1524 \r
1525                 Wait(g->FlushEvent, 100);\r
1526         }\r
1527 }\r
1528 \r
1529 // ログ記録用スレッド\r
1530 void LogThread(THREAD *thread, void *param)\r
1531 {\r
1532         LOG *g;\r
1533         IO *io;\r
1534         BUF *b;\r
1535         bool flag = false;\r
1536         char current_file_name[MAX_SIZE];\r
1537         char current_logfile_datename[MAX_SIZE];\r
1538         bool last_priority_flag = false;\r
1539         bool log_date_changed = false;\r
1540         // 引数チェック\r
1541         if (thread == NULL || param == NULL)\r
1542         {\r
1543                 return;\r
1544         }\r
1545 \r
1546         Zero(current_file_name, sizeof(current_file_name));\r
1547         Zero(current_logfile_datename, sizeof(current_logfile_datename));\r
1548 \r
1549         g = (LOG *)param;\r
1550 \r
1551         io = NULL;\r
1552         b = NewBuf();\r
1553 \r
1554 #ifdef  OS_WIN32\r
1555 \r
1556         // 優先順位を最低にする\r
1557         MsSetThreadPriorityIdle();\r
1558 \r
1559 #endif  // OS_WIN32\r
1560 \r
1561         NoticeThreadInit(thread);\r
1562 \r
1563         while (true)\r
1564         {\r
1565                 RECORD *rec;\r
1566                 UINT64 s = Tick64();\r
1567 \r
1568                 while (true)\r
1569                 {\r
1570                         char file_name[MAX_SIZE];\r
1571                         UINT num;\r
1572 \r
1573                         // キューの先頭からレコードを取得する\r
1574                         LockQueue(g->RecordQueue);\r
1575                         {\r
1576                                 rec = GetNext(g->RecordQueue);\r
1577                                 num = g->RecordQueue->num_item;\r
1578                         }\r
1579                         UnlockQueue(g->RecordQueue);\r
1580 \r
1581 #ifdef  OS_WIN32\r
1582                         if (num >= LOG_ENGINE_SAVE_START_CACHE_COUNT)\r
1583                         {\r
1584                                 // 優先順位を上げる\r
1585                                 if (last_priority_flag == false)\r
1586                                 {\r
1587                                         Debug("LOG_THREAD: MsSetThreadPriorityRealtime\n");\r
1588                                         MsSetThreadPriorityRealtime();\r
1589                                         last_priority_flag = true;\r
1590                                 }\r
1591                         }\r
1592 \r
1593                         if (num < (LOG_ENGINE_SAVE_START_CACHE_COUNT / 2))\r
1594                         {\r
1595                                 // 優先順位を戻す\r
1596                                 if (last_priority_flag)\r
1597                                 {\r
1598                                         Debug("LOG_THREAD: MsSetThreadPriorityIdle\n");\r
1599                                         MsSetThreadPriorityIdle();\r
1600                                         last_priority_flag = false;\r
1601                                 }\r
1602                         }\r
1603 #endif  // OS_WIN32\r
1604 \r
1605                         if (b->Size > g->MaxLogFileSize)\r
1606                         {\r
1607                                 // バッファのサイズが最大ログファイルサイズを超える場合は消去する\r
1608                                 ClearBuf(b);\r
1609                         }\r
1610 \r
1611                         if (b->Size >= LOG_ENGINE_BUFFER_CACHE_SIZE_MAX)\r
1612                         {\r
1613                                 // バッファの中身をファイルに書き出す\r
1614                                 if (io != NULL)\r
1615                                 {\r
1616                                         if ((g->CurrentFilePointer + (UINT64)b->Size) > g->MaxLogFileSize)\r
1617                                         {\r
1618                                                 if (g->log_number_incremented == false)\r
1619                                                 {\r
1620                                                         g->CurrentLogNumber++;\r
1621                                                         g->log_number_incremented = true;\r
1622                                                 }\r
1623                                         }\r
1624                                         else\r
1625                                         {\r
1626                                                 if (FileWrite(io, b->Buf, b->Size) == false)\r
1627                                                 {\r
1628                                                         FileCloseEx(io, true);\r
1629                                                         // ファイルへの書き込みに失敗した場合は仕方が無い\r
1630                                                         // のでバッファを消して諦める\r
1631                                                         ClearBuf(b);\r
1632                                                         io = NULL;\r
1633                                                 }\r
1634                                                 else\r
1635                                                 {\r
1636                                                         g->CurrentFilePointer += (UINT64)b->Size;\r
1637                                                         ClearBuf(b);\r
1638                                                 }\r
1639                                         }\r
1640                                 }\r
1641                         }\r
1642 \r
1643                         if (rec == NULL)\r
1644                         {\r
1645                                 if (b->Size != 0)\r
1646                                 {\r
1647                                         // バッファの中身をファイルに書き出す\r
1648                                         if (io != NULL)\r
1649                                         {\r
1650                                                 if ((g->CurrentFilePointer + (UINT64)b->Size) > g->MaxLogFileSize)\r
1651                                                 {\r
1652                                                         if (g->log_number_incremented == false)\r
1653                                                         {\r
1654                                                                 g->CurrentLogNumber++;\r
1655                                                                 g->log_number_incremented = true;\r
1656                                                         }\r
1657                                                 }\r
1658                                                 else\r
1659                                                 {\r
1660                                                         if (FileWrite(io, b->Buf, b->Size) == false)\r
1661                                                         {\r
1662                                                                 FileCloseEx(io, true);\r
1663                                                                 // ファイルへの書き込みに失敗した場合は仕方が無い\r
1664                                                                 // のでバッファを消して諦める\r
1665                                                                 ClearBuf(b);\r
1666                                                                 io = NULL;\r
1667                                                         }\r
1668                                                         else\r
1669                                                         {\r
1670                                                                 g->CurrentFilePointer += (UINT64)b->Size;\r
1671                                                                 ClearBuf(b);\r
1672                                                         }\r
1673                                                 }\r
1674                                         }\r
1675                                 }\r
1676 \r
1677                                 Set(g->FlushEvent);\r
1678                                 break;\r
1679                         }\r
1680 \r
1681                         // ログファイル名を生成する\r
1682                         LockLog(g);\r
1683                         {\r
1684                                 log_date_changed = MakeLogFileName(g, file_name, sizeof(file_name),\r
1685                                         g->DirName, g->Prefix, rec->Tick, g->SwitchType, g->CurrentLogNumber, current_logfile_datename);\r
1686 \r
1687                                 if (log_date_changed)\r
1688                                 {\r
1689                                         UINT i;\r
1690 \r
1691                                         g->CurrentLogNumber = 0;\r
1692                                         MakeLogFileName(g, file_name, sizeof(file_name),\r
1693                                                 g->DirName, g->Prefix, rec->Tick, g->SwitchType, 0, current_logfile_datename);\r
1694                                         for (i = 0;;i++)\r
1695                                         {\r
1696                                                 char tmp[MAX_SIZE];\r
1697                                                 MakeLogFileName(g, tmp, sizeof(tmp),\r
1698                                                         g->DirName, g->Prefix, rec->Tick, g->SwitchType, i, current_logfile_datename);\r
1699 \r
1700                                                 if (IsFileExists(tmp) == false)\r
1701                                                 {\r
1702                                                         break;\r
1703                                                 }\r
1704                                                 StrCpy(file_name, sizeof(file_name), tmp);\r
1705                                                 g->CurrentLogNumber = i;\r
1706                                         }\r
1707                                 }\r
1708                         }\r
1709                         UnlockLog(g);\r
1710 \r
1711                         if (io != NULL)\r
1712                         {\r
1713                                 if (StrCmp(current_file_name, file_name) != 0)\r
1714                                 {\r
1715                                         // 現在ログファイルを開いていて今回別のログファイルへの書き込みが必要になった\r
1716                                         // 場合はログファイルにバッファの内容を書き込んでからログファイルを閉じる\r
1717                                         // バッファの中身をファイルに書き出す\r
1718                                         if (io != NULL)\r
1719                                         {\r
1720                                                 if (log_date_changed)\r
1721                                                 {\r
1722                                                         if ((g->CurrentFilePointer + (UINT64)b->Size) <= g->MaxLogFileSize)\r
1723                                                         {\r
1724                                                                 if (FileWrite(io, b->Buf, b->Size) == false)\r
1725                                                                 {\r
1726                                                                         FileCloseEx(io, true);\r
1727                                                                         ClearBuf(b);\r
1728                                                                         io = NULL;\r
1729                                                                 }\r
1730                                                                 else\r
1731                                                                 {\r
1732                                                                         g->CurrentFilePointer += (UINT64)b->Size;\r
1733                                                                         ClearBuf(b);\r
1734                                                                 }\r
1735                                                         }\r
1736                                                 }\r
1737                                                 // ファイルを閉じる\r
1738                                                 FileCloseEx(io, true);\r
1739                                         }\r
1740 \r
1741                                         g->log_number_incremented = false;\r
1742 \r
1743                                         // 新しいログファイルを開くか作成する\r
1744                                         StrCpy(current_file_name, sizeof(current_file_name), file_name);\r
1745                                         io = FileOpen(file_name, true);\r
1746                                         if (io == NULL)\r
1747                                         {\r
1748                                                 // ログファイルを作成する\r
1749                                                 LockLog(g);\r
1750                                                 {\r
1751                                                         MakeDir(g->DirName);\r
1752 \r
1753 #ifdef  OS_WIN32\r
1754                                                         Win32SetFolderCompress(g->DirName, true);\r
1755 #endif  // OS_WIN32\r
1756                                                 }\r
1757                                                 UnlockLog(g);\r
1758                                                 io = FileCreate(file_name);\r
1759                                                 g->CurrentFilePointer = 0;\r
1760                                         }\r
1761                                         else\r
1762                                         {\r
1763                                                 // ログファイルの末尾に移動する\r
1764                                                 g->CurrentFilePointer = FileSize64(io);\r
1765                                                 FileSeek(io, SEEK_END, 0);\r
1766                                         }\r
1767                                 }\r
1768                         }\r
1769                         else\r
1770                         {\r
1771                                 // 新しいログファイルを開くか作成する\r
1772                                 StrCpy(current_file_name, sizeof(current_file_name), file_name);\r
1773                                 io = FileOpen(file_name, true);\r
1774                                 if (io == NULL)\r
1775                                 {\r
1776                                         // ログファイルを作成する\r
1777                                         LockLog(g);\r
1778                                         {\r
1779                                                 MakeDir(g->DirName);\r
1780 #ifdef  OS_WIN32\r
1781                                                 Win32SetFolderCompress(g->DirName, true);\r
1782 #endif  // OS_WIN32\r
1783                                         }\r
1784                                         UnlockLog(g);\r
1785                                         io = FileCreate(file_name);\r
1786                                         g->CurrentFilePointer = 0;\r
1787                                         if (io == NULL)\r
1788                                         {\r
1789                                                 Debug("Logging.c: SleepThread(30);\n");\r
1790                                                 SleepThread(30);\r
1791                                         }\r
1792                                 }\r
1793                                 else\r
1794                                 {\r
1795                                         // ログファイルの末尾に移動する\r
1796                                         g->CurrentFilePointer = FileSize64(io);\r
1797                                         FileSeek(io, SEEK_END, 0);\r
1798                                 }\r
1799 \r
1800                                 g->log_number_incremented = false;\r
1801                         }\r
1802 \r
1803                         // ログの内容をバッファに書き出す\r
1804                         WriteRecordToBuffer(b, rec);\r
1805 \r
1806                         // レコードのメモリを解放\r
1807                         Free(rec);\r
1808 \r
1809                         if (io == NULL)\r
1810                         {\r
1811                                 break;\r
1812                         }\r
1813                 }\r
1814 \r
1815                 if (g->Halt)\r
1816                 {\r
1817                         // 停止フラグが立った場合\r
1818                         // すべてのレコードを保存し終えるとブレイクする\r
1819                         UINT num;\r
1820 \r
1821                         if (flag == false)\r
1822                         {\r
1823 #ifdef  OS_WIN32\r
1824                                 MsSetThreadPriorityRealtime();\r
1825 #endif  // OS_WIN32\r
1826                                 flag = true;\r
1827                         }\r
1828 \r
1829                         LockQueue(g->RecordQueue);\r
1830                         {\r
1831                                 num = g->RecordQueue->num_item;\r
1832                         }\r
1833                         UnlockQueue(g->RecordQueue);\r
1834 \r
1835                         if (num == 0 || io == NULL)\r
1836                         {\r
1837                                 break;\r
1838                         }\r
1839                 }\r
1840                 else\r
1841                 {\r
1842                         Wait(g->Event, 9821);\r
1843                 }\r
1844         }\r
1845 \r
1846         if (io != NULL)\r
1847         {\r
1848                 FileCloseEx(io, true);\r
1849         }\r
1850 \r
1851         FreeBuf(b);\r
1852 }\r
1853 \r
1854 // ログの内容をバッファに書き出す\r
1855 void WriteRecordToBuffer(BUF *b, RECORD *r)\r
1856 {\r
1857         UINT64 time;\r
1858         char time_str[MAX_SIZE];\r
1859         char date_str[MAX_SIZE];\r
1860         char *s;\r
1861         // 引数チェック\r
1862         if (b == NULL || r == NULL)\r
1863         {\r
1864                 return;\r
1865         }\r
1866 \r
1867         // 時刻の取得\r
1868         time = SystemToLocal64(TickToTime(r->Tick));\r
1869 \r
1870         // 時刻を文字列に変換\r
1871         GetDateStr64(date_str, sizeof(date_str), time);\r
1872         GetTimeStrMilli64(time_str, sizeof(time_str), time);\r
1873 \r
1874         if (r->ParseProc != PacketLogParseProc)\r
1875         {\r
1876                 // パケットログ以外\r
1877                 WriteBuf(b, date_str, StrLen(date_str));\r
1878                 WriteBuf(b, " ", 1);\r
1879                 WriteBuf(b, time_str, StrLen(time_str));\r
1880                 WriteBuf(b, " ", 1);\r
1881         }\r
1882         else\r
1883         {\r
1884                 // パケットログ\r
1885                 WriteBuf(b, date_str, StrLen(date_str));\r
1886                 WriteBuf(b, ",", 1);\r
1887                 WriteBuf(b, time_str, StrLen(time_str));\r
1888                 WriteBuf(b, ",", 1);\r
1889         }\r
1890 \r
1891         // 本文を出力\r
1892         s = r->ParseProc(r);\r
1893         WriteBuf(b, s, StrLen(s));\r
1894         Free(s);\r
1895 \r
1896         WriteBuf(b, "\r\n", 2);\r
1897 }\r
1898 \r
1899 // ログ記録の終了\r
1900 void FreeLog(LOG *g)\r
1901 {\r
1902         RECORD *rec;\r
1903         // 引数チェック\r
1904         if (g == NULL)\r
1905         {\r
1906                 return;\r
1907         }\r
1908 \r
1909         // 停止フラグ\r
1910         g->Halt = true;\r
1911         Set(g->Event);\r
1912 \r
1913         WaitThread(g->Thread, INFINITE);\r
1914         ReleaseThread(g->Thread);\r
1915 \r
1916         DeleteLock(g->lock);\r
1917         Free(g->DirName);\r
1918         Free(g->Prefix);\r
1919 \r
1920         // 未処理のレコードが残っている場合は解放する\r
1921         // (本来はここでは残っていないはず)\r
1922         while (rec = GetNext(g->RecordQueue))\r
1923         {\r
1924                 char *s = rec->ParseProc(rec);\r
1925                 Free(s);\r
1926                 Free(rec);\r
1927         }\r
1928         ReleaseQueue(g->RecordQueue);\r
1929 \r
1930         ReleaseEvent(g->Event);\r
1931         ReleaseEvent(g->FlushEvent);\r
1932 \r
1933         Free(g);\r
1934 }\r
1935 \r
1936 // 新しいログ記録の開始\r
1937 LOG *NewLog(char *dir, char *prefix, UINT switch_type)\r
1938 {\r
1939         LOG *g;\r
1940 \r
1941         g = ZeroMalloc(sizeof(LOG));\r
1942         g->lock = NewLock();\r
1943         g->DirName = CopyStr(dir == NULL ? "" : dir);\r
1944         g->Prefix = CopyStr(prefix == NULL ? "log" : prefix);\r
1945         g->SwitchType = switch_type;\r
1946         g->RecordQueue = NewQueue();\r
1947         g->Event = NewEvent();\r
1948         g->MaxLogFileSize = MAX_LOG_SIZE;\r
1949         g->FlushEvent = NewEvent();\r
1950 \r
1951         g->Thread = NewThread(LogThread, g);\r
1952 \r
1953         WaitThreadInit(g->Thread);\r
1954 \r
1955         return g;\r
1956 }\r
1957 \r
1958 \r