mirror of
https://github.com/SoftEtherVPN/SoftEtherVPN.git
synced 2024-11-14 05:30:41 +03:00
ac865f04fc
* spelling: accepts * spelling: account * spelling: accept * spelling: accumulate * spelling: adapter * spelling: address * spelling: additional * spelling: aggressive * spelling: adhered * spelling: allowed * spelling: ambiguous * spelling: amount * spelling: anonymous * spelling: acquisition * spelling: assemble * spelling: associated * spelling: assigns * spelling: attach * spelling: attempt * spelling: attribute * spelling: authenticate * spelling: authentication * spelling: available * spelling: bridging * spelling: cascade * spelling: cancel * spelling: check * spelling: challenge * spelling: changing * spelling: characters * spelling: cloud * spelling: compare * spelling: communication * spelling: compatible * spelling: compatibility * spelling: completion * spelling: complete * spelling: computers * spelling: configure * spelling: configuration * spelling: conformant * spelling: connection * spelling: contains * spelling: continuously * spelling: continue * spelling: convert * spelling: counters * spelling: create * spelling: created * spelling: cumulate * spelling: currently * spelling: debugging * spelling: decryption * spelling: description * spelling: default * spelling: driver * spelling: delete * spelling: destination * spelling: disabled * spelling: different * spelling: dynamically * spelling: directory * spelling: disappeared * spelling: disable * spelling: doesn't * spelling: download * spelling: dropped * spelling: enable * spelling: established * spelling: ether * spelling: except * spelling: expired * spelling: field * spelling: following * spelling: forever * spelling: firewall * spelling: first * spelling: fragment * spelling: function * spelling: gateway * spelling: identifier * spelling: identify * spelling: incoming * spelling: information * spelling: initialize * spelling: injection * spelling: inner * spelling: instead * spelling: installation * spelling: inserted * spelling: integer * spelling: interrupt * spelling: intuitive * spelling: interval * spelling: january * spelling: keybytes * spelling: know * spelling: language * spelling: length * spelling: library * spelling: listener * spelling: maintain * spelling: modified * spelling: necessary * spelling: number * spelling: obsoleted * spelling: occurred * spelling: occurring * spelling: occur * spelling: original * spelling: omittable * spelling: omit * spelling: opening * spelling: operation * spelling: packet * spelling: parameters * spelling: pointed * spelling: popupmenuopen * spelling: privilege * spelling: product * spelling: protection * spelling: promiscuous * spelling: prompt * spelling: query * spelling: random * spelling: reconnection * spelling: revocation * spelling: received * spelling: red hat * spelling: registry * spelling: release * spelling: retrieve
2336 lines
48 KiB
C
2336 lines
48 KiB
C
// SoftEther VPN Source Code - Developer Edition Master Branch
|
|
// Mayaqua Kernel
|
|
//
|
|
// SoftEther VPN Server, Client and Bridge are free software under GPLv2.
|
|
//
|
|
// Copyright (c) Daiyuu Nobori.
|
|
// Copyright (c) SoftEther VPN Project, University of Tsukuba, Japan.
|
|
// Copyright (c) SoftEther Corporation.
|
|
//
|
|
// All Rights Reserved.
|
|
//
|
|
// http://www.softether.org/
|
|
//
|
|
// Author: Daiyuu Nobori, Ph.D.
|
|
// Contributors:
|
|
// - nattoheaven (https://github.com/nattoheaven)
|
|
// Comments: Tetsuo Sugiyama, Ph.D.
|
|
//
|
|
// This program is free software; you can redistribute it and/or
|
|
// modify it under the terms of the GNU General Public License
|
|
// version 2 as published by the Free Software Foundation.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License version 2
|
|
// along with this program; if not, write to the Free Software
|
|
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
|
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
|
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
|
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
//
|
|
// THE LICENSE AGREEMENT IS ATTACHED ON THE SOURCE-CODE PACKAGE
|
|
// AS "LICENSE.TXT" FILE. READ THE TEXT FILE IN ADVANCE TO USE THE SOFTWARE.
|
|
//
|
|
//
|
|
// THIS SOFTWARE IS DEVELOPED IN JAPAN, AND DISTRIBUTED FROM JAPAN,
|
|
// UNDER JAPANESE LAWS. YOU MUST AGREE IN ADVANCE TO USE, COPY, MODIFY,
|
|
// MERGE, PUBLISH, DISTRIBUTE, SUBLICENSE, AND/OR SELL COPIES OF THIS
|
|
// SOFTWARE, THAT ANY JURIDICAL DISPUTES WHICH ARE CONCERNED TO THIS
|
|
// SOFTWARE OR ITS CONTENTS, AGAINST US (SOFTETHER PROJECT, SOFTETHER
|
|
// CORPORATION, DAIYUU NOBORI OR OTHER SUPPLIERS), OR ANY JURIDICAL
|
|
// DISPUTES AGAINST US WHICH ARE CAUSED BY ANY KIND OF USING, COPYING,
|
|
// MODIFYING, MERGING, PUBLISHING, DISTRIBUTING, SUBLICENSING, AND/OR
|
|
// SELLING COPIES OF THIS SOFTWARE SHALL BE REGARDED AS BE CONSTRUED AND
|
|
// CONTROLLED BY JAPANESE LAWS, AND YOU MUST FURTHER CONSENT TO
|
|
// EXCLUSIVE JURISDICTION AND VENUE IN THE COURTS SITTING IN TOKYO,
|
|
// JAPAN. YOU MUST WAIVE ALL DEFENSES OF LACK OF PERSONAL JURISDICTION
|
|
// AND FORUM NON CONVENIENS. PROCESS MAY BE SERVED ON EITHER PARTY IN
|
|
// THE MANNER AUTHORIZED BY APPLICABLE LAW OR COURT RULE.
|
|
//
|
|
// USE ONLY IN JAPAN. DO NOT USE THIS SOFTWARE IN ANOTHER COUNTRY UNLESS
|
|
// YOU HAVE A CONFIRMATION THAT THIS SOFTWARE DOES NOT VIOLATE ANY
|
|
// CRIMINAL LAWS OR CIVIL RIGHTS IN THAT PARTICULAR COUNTRY. USING THIS
|
|
// SOFTWARE IN OTHER COUNTRIES IS COMPLETELY AT YOUR OWN RISK. THE
|
|
// SOFTETHER VPN PROJECT HAS DEVELOPED AND DISTRIBUTED THIS SOFTWARE TO
|
|
// COMPLY ONLY WITH THE JAPANESE LAWS AND EXISTING CIVIL RIGHTS INCLUDING
|
|
// PATENTS WHICH ARE SUBJECTS APPLY IN JAPAN. OTHER COUNTRIES' LAWS OR
|
|
// CIVIL RIGHTS ARE NONE OF OUR CONCERNS NOR RESPONSIBILITIES. WE HAVE
|
|
// NEVER INVESTIGATED ANY CRIMINAL REGULATIONS, CIVIL LAWS OR
|
|
// INTELLECTUAL PROPERTY RIGHTS INCLUDING PATENTS IN ANY OF OTHER 200+
|
|
// COUNTRIES AND TERRITORIES. BY NATURE, THERE ARE 200+ REGIONS IN THE
|
|
// WORLD, WITH DIFFERENT LAWS. IT IS IMPOSSIBLE TO VERIFY EVERY
|
|
// COUNTRIES' LAWS, REGULATIONS AND CIVIL RIGHTS TO MAKE THE SOFTWARE
|
|
// COMPLY WITH ALL COUNTRIES' LAWS BY THE PROJECT. EVEN IF YOU WILL BE
|
|
// SUED BY A PRIVATE ENTITY OR BE DAMAGED BY A PUBLIC SERVANT IN YOUR
|
|
// COUNTRY, THE DEVELOPERS OF THIS SOFTWARE WILL NEVER BE LIABLE TO
|
|
// RECOVER OR COMPENSATE SUCH DAMAGES, CRIMINAL OR CIVIL
|
|
// RESPONSIBILITIES. NOTE THAT THIS LINE IS NOT LICENSE RESTRICTION BUT
|
|
// JUST A STATEMENT FOR WARNING AND DISCLAIMER.
|
|
//
|
|
//
|
|
// SOURCE CODE CONTRIBUTION
|
|
// ------------------------
|
|
//
|
|
// Your contribution to SoftEther VPN Project is much appreciated.
|
|
// Please send patches to us through GitHub.
|
|
// Read the SoftEther VPN Patch Acceptance Policy in advance:
|
|
// http://www.softether.org/5-download/src/9.patch
|
|
//
|
|
//
|
|
// DEAR SECURITY EXPERTS
|
|
// ---------------------
|
|
//
|
|
// If you find a bug or a security vulnerability please kindly inform us
|
|
// about the problem immediately so that we can fix the security problem
|
|
// to protect a lot of users around the world as soon as possible.
|
|
//
|
|
// Our e-mail address for security reports is:
|
|
// softether-vpn-security [at] softether.org
|
|
//
|
|
// Please note that the above e-mail address is not a technical support
|
|
// inquiry address. If you need technical assistance, please visit
|
|
// http://www.softether.org/ and ask your question on the users forum.
|
|
//
|
|
// Thank you for your cooperation.
|
|
//
|
|
//
|
|
// NO MEMORY OR RESOURCE LEAKS
|
|
// ---------------------------
|
|
//
|
|
// The memory-leaks and resource-leaks verification under the stress
|
|
// test has been passed before release this source code.
|
|
|
|
|
|
// Kernel.c
|
|
// System service processing routine
|
|
|
|
#include <GlobalConst.h>
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <wchar.h>
|
|
#include <stdarg.h>
|
|
#include <time.h>
|
|
#include <errno.h>
|
|
#include <Mayaqua/Mayaqua.h>
|
|
|
|
#ifndef TM_YEAR_MAX
|
|
#define TM_YEAR_MAX 2106
|
|
#endif
|
|
#ifndef TM_MON_MAX
|
|
#define TM_MON_MAX 1
|
|
#endif
|
|
#ifndef TM_MDAY_MAX
|
|
#define TM_MDAY_MAX 7
|
|
#endif
|
|
#ifndef TM_HOUR_MAX
|
|
#define TM_HOUR_MAX 6
|
|
#endif
|
|
#ifndef TM_MIN_MAX
|
|
#define TM_MIN_MAX 28
|
|
#endif
|
|
#ifndef TM_SEC_MAX
|
|
#define TM_SEC_MAX 14
|
|
#endif
|
|
|
|
#define ADJUST_TM(tm_member, tm_carry, modulus) \
|
|
if ((tm_member) < 0){ \
|
|
tm_carry -= (1 - ((tm_member)+1) / (modulus)); \
|
|
tm_member = (modulus-1) + (((tm_member)+1) % (modulus)); \
|
|
} else if ((tm_member) >= (modulus)) { \
|
|
tm_carry += (tm_member) / (modulus); \
|
|
tm_member = (tm_member) % (modulus); \
|
|
}
|
|
#define leap(y) (((y) % 4 == 0 && (y) % 100 != 0) || (y) % 400 == 0)
|
|
#define nleap(y) (((y) - 1969) / 4 - ((y) - 1901) / 100 + ((y) - 1601) / 400)
|
|
#define leapday(m, y) ((m) == 1 && leap (y))
|
|
#define monthlen(m, y) (ydays[(m)+1] - ydays[m] + leapday (m, y))
|
|
static int ydays[] =
|
|
{
|
|
0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365
|
|
};
|
|
|
|
static UINT current_num_thread = 0;
|
|
static UINT cached_number_of_cpus = 0;
|
|
|
|
|
|
|
|
static wchar_t *default_locale_str =
|
|
L"- - $ : : $ Sun Mon Tue Wed Thu Fri Sat : : : $ (None)";
|
|
|
|
|
|
static LOCALE current_locale;
|
|
LOCK *tick_manual_lock = NULL;
|
|
UINT g_zero = 0;
|
|
|
|
#define MONSPERYEAR 12
|
|
#define DAYSPERNYEAR 365
|
|
#define DAYSPERLYEAR 366
|
|
#define SECSPERMIN 60
|
|
#define SECSPERHOUR (60*60)
|
|
#define SECSPERDAY (24*60*60)
|
|
#define DAYSPERWEEK 7
|
|
#define TM_SUNDAY 0
|
|
#define TM_MONDAY 1
|
|
#define TM_TUESDAY 2
|
|
#define TM_WEDNESDAY 3
|
|
#define TM_THURSDAY 4
|
|
#define TM_FRIDAY 5
|
|
#define TM_SATURDAY 6
|
|
|
|
#define TM_YEAR_BASE 1900
|
|
|
|
#define EPOCH_YEAR 1970
|
|
#define EPOCH_WDAY TM_THURSDAY
|
|
|
|
#define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0))
|
|
|
|
static const int mon_lengths[2][MONSPERYEAR] = {
|
|
{ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
|
|
{ 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
|
|
};
|
|
|
|
static const int year_lengths[2] = {
|
|
DAYSPERNYEAR, DAYSPERLYEAR
|
|
};
|
|
|
|
|
|
/*
|
|
* Taken from FreeBSD src / lib / libc / stdtime / localtime.c 1.43 revision.
|
|
* localtime.c 7.78.
|
|
* tzfile.h 1.8
|
|
* adapted to be replacement gmtime_r.
|
|
*/
|
|
static void
|
|
c_timesub(timep, offset, tmp)
|
|
const time_64t * const timep;
|
|
const long offset;
|
|
struct tm * const tmp;
|
|
{
|
|
INT64 days;
|
|
INT64 rem;
|
|
INT64 y;
|
|
int yleap;
|
|
const int * ip;
|
|
|
|
days = *timep / SECSPERDAY;
|
|
rem = *timep % SECSPERDAY;
|
|
rem += (offset);
|
|
while (rem < 0) {
|
|
rem += SECSPERDAY;
|
|
--days;
|
|
}
|
|
while (rem >= SECSPERDAY) {
|
|
rem -= SECSPERDAY;
|
|
++days;
|
|
}
|
|
tmp->tm_hour = (int) (rem / SECSPERHOUR);
|
|
rem = rem % SECSPERHOUR;
|
|
tmp->tm_min = (int) (rem / SECSPERMIN);
|
|
/*
|
|
** A positive leap second requires a special
|
|
** representation. This uses "... ??:59:60" et seq.
|
|
*/
|
|
tmp->tm_sec = (int) (rem % SECSPERMIN) ;
|
|
tmp->tm_wday = (int) ((EPOCH_WDAY + days) % DAYSPERWEEK);
|
|
if (tmp->tm_wday < 0)
|
|
tmp->tm_wday += DAYSPERWEEK;
|
|
y = EPOCH_YEAR;
|
|
#define LEAPS_THRU_END_OF(y) ((y) / 4 - (y) / 100 + (y) / 400)
|
|
while (days < 0 || days >= (long) year_lengths[yleap = isleap(y)]) {
|
|
INT64 newy;
|
|
|
|
newy = y + days / DAYSPERNYEAR;
|
|
if (days < 0)
|
|
--newy;
|
|
days -= (newy - y) * DAYSPERNYEAR +
|
|
LEAPS_THRU_END_OF(newy - 1) -
|
|
LEAPS_THRU_END_OF(y - 1);
|
|
y = newy;
|
|
}
|
|
tmp->tm_year = (int)(y - TM_YEAR_BASE);
|
|
tmp->tm_yday = (int) days;
|
|
ip = mon_lengths[yleap];
|
|
for (tmp->tm_mon = 0; days >= (INT64) ip[tmp->tm_mon]; ++(tmp->tm_mon))
|
|
days = days - (INT64) ip[tmp->tm_mon];
|
|
tmp->tm_mday = (int) (days + 1);
|
|
tmp->tm_isdst = 0;
|
|
}
|
|
|
|
/*
|
|
* Re-entrant version of gmtime.
|
|
*/
|
|
struct tm * c_gmtime_r(const time_64t* timep, struct tm *tm)
|
|
{
|
|
c_timesub(timep, 0L, tm);
|
|
return tm;
|
|
}
|
|
|
|
// Get the real-time system timer
|
|
UINT TickRealtime()
|
|
{
|
|
#if defined(OS_WIN32) || defined(CLOCK_REALTIME) || defined(CLOCK_MONOTONIC) || defined(CLOCK_HIGHRES) || defined(UNIX_MACOS)
|
|
return Tick() + 1;
|
|
#else
|
|
return TickRealtimeManual() + 1;
|
|
#endif
|
|
}
|
|
|
|
#ifndef OS_WIN32
|
|
|
|
static UINT64 last_manual_tick = 0;
|
|
static UINT64 manual_tick_add_value = 0;
|
|
|
|
// For systems which not have clock_gettime (such as MacOS X)
|
|
UINT TickRealtimeManual()
|
|
{
|
|
UINT64 ret;
|
|
Lock(tick_manual_lock);
|
|
{
|
|
ret = TickGetRealtimeTickValue64();
|
|
|
|
if (last_manual_tick != 0 && (last_manual_tick > ret))
|
|
{
|
|
manual_tick_add_value += (last_manual_tick - ret);
|
|
}
|
|
|
|
last_manual_tick = ret;
|
|
}
|
|
Unlock(tick_manual_lock);
|
|
|
|
return (UINT)(ret + manual_tick_add_value);
|
|
}
|
|
|
|
// Returns a appropriate value from the current time
|
|
UINT64 TickGetRealtimeTickValue64()
|
|
{
|
|
struct timeval tv;
|
|
struct timezone tz;
|
|
UINT64 ret;
|
|
|
|
memset(&tv, 0, sizeof(tv));
|
|
memset(&tz, 0, sizeof(tz));
|
|
|
|
gettimeofday(&tv, &tz);
|
|
|
|
if (sizeof(tv.tv_sec) != 4)
|
|
{
|
|
ret = (UINT64)tv.tv_sec * 1000ULL + (UINT64)tv.tv_usec / 1000ULL;
|
|
}
|
|
else
|
|
{
|
|
ret = (UINT64)((UINT64)((UINT32)tv.tv_sec)) * 1000ULL + (UINT64)tv.tv_usec / 1000ULL;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
#endif // OS_WIN32
|
|
|
|
// Get the number of CPUs
|
|
UINT GetNumberOfCpu()
|
|
{
|
|
UINT ret = 0;
|
|
|
|
if (cached_number_of_cpus == 0)
|
|
{
|
|
UINT i = 0;
|
|
|
|
#ifdef OS_WIN32
|
|
i = Win32GetNumberOfCpuInner();
|
|
#else // OS_WIN32
|
|
i = UnixGetNumberOfCpuInner();
|
|
#endif // OS_WIN32
|
|
|
|
if (i == 0)
|
|
{
|
|
i = 8;
|
|
}
|
|
|
|
cached_number_of_cpus = i;
|
|
}
|
|
|
|
ret = cached_number_of_cpus;
|
|
|
|
if (ret == 0)
|
|
{
|
|
ret = 1;
|
|
}
|
|
if (ret > 128)
|
|
{
|
|
ret = 128;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
// Creating a thread list
|
|
LIST *NewThreadList()
|
|
{
|
|
LIST *o = NewList(NULL);
|
|
|
|
return o;
|
|
}
|
|
|
|
// Remove the thread from the thread list
|
|
void DelThreadFromThreadList(LIST *o, THREAD *t)
|
|
{
|
|
// Validate arguments
|
|
if (o == NULL || t == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
LockList(o);
|
|
{
|
|
if (Delete(o, t))
|
|
{
|
|
ReleaseThread(t);
|
|
}
|
|
}
|
|
UnlockList(o);
|
|
}
|
|
|
|
// Add the thread to the thread list
|
|
void AddThreadToThreadList(LIST *o, THREAD *t)
|
|
{
|
|
// Validate arguments
|
|
if (o == NULL || t == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
LockList(o);
|
|
{
|
|
if (IsInList(o, t) == false)
|
|
{
|
|
AddRef(t->ref);
|
|
|
|
Add(o, t);
|
|
}
|
|
}
|
|
UnlockList(o);
|
|
}
|
|
|
|
// Maintain thread list
|
|
void MaintainThreadList(LIST *o)
|
|
{
|
|
UINT i;
|
|
LIST *delete_list = NULL;
|
|
// Validate arguments
|
|
if (o == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
LockList(o);
|
|
{
|
|
for (i = 0;i < LIST_NUM(o);i++)
|
|
{
|
|
THREAD *t = LIST_DATA(o, i);
|
|
|
|
if (t->Stopped)
|
|
{
|
|
if (delete_list == NULL)
|
|
{
|
|
delete_list = NewListFast(NULL);
|
|
}
|
|
|
|
Add(delete_list, t);
|
|
}
|
|
}
|
|
|
|
if (delete_list != NULL)
|
|
{
|
|
for (i = 0;i < LIST_NUM(delete_list);i++)
|
|
{
|
|
THREAD *t = LIST_DATA(delete_list, i);
|
|
|
|
ReleaseThread(t);
|
|
|
|
Delete(o, t);
|
|
}
|
|
|
|
ReleaseList(delete_list);
|
|
}
|
|
}
|
|
UnlockList(o);
|
|
}
|
|
|
|
// Wait until all threads in the thread list will be stopped
|
|
void WaitAllThreadsWillBeStopped(LIST *o)
|
|
{
|
|
// Validate arguments
|
|
if (o == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
while (LIST_NUM(o) != 0)
|
|
{
|
|
SleepThread(100);
|
|
}
|
|
}
|
|
|
|
// Stop all the threads in the thread list
|
|
void StopThreadList(LIST *o)
|
|
{
|
|
UINT i;
|
|
// Validate arguments
|
|
if (o == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
LockList(o);
|
|
{
|
|
for (i = 0;i < LIST_NUM(o);i++)
|
|
{
|
|
THREAD *t = LIST_DATA(o, i);
|
|
|
|
WaitThread(t, INFINITE);
|
|
}
|
|
}
|
|
UnlockList(o);
|
|
}
|
|
|
|
// Release the thread list
|
|
void FreeThreadList(LIST *o)
|
|
{
|
|
UINT i;
|
|
// Validate arguments
|
|
if (o == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
LockList(o);
|
|
{
|
|
for (i = 0;i < LIST_NUM(o);i++)
|
|
{
|
|
THREAD *t = LIST_DATA(o, i);
|
|
|
|
WaitThread(t, INFINITE);
|
|
|
|
ReleaseThread(t);
|
|
}
|
|
|
|
DeleteAll(o);
|
|
}
|
|
UnlockList(o);
|
|
|
|
ReleaseList(o);
|
|
}
|
|
|
|
// Get the home directory
|
|
void GetHomeDirW(wchar_t *path, UINT size)
|
|
{
|
|
// Validate arguments
|
|
if (path == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (GetEnvW(L"HOME", path, size) == false)
|
|
{
|
|
wchar_t drive[MAX_SIZE];
|
|
wchar_t hpath[MAX_SIZE];
|
|
if (GetEnvW(L"HOMEDRIVE", drive, sizeof(drive)) &&
|
|
GetEnvW(L"HOMEPATH", hpath, sizeof(hpath)))
|
|
{
|
|
UniFormat(path, sizeof(path), L"%s%s", drive, hpath);
|
|
}
|
|
else
|
|
{
|
|
#ifdef OS_WIN32
|
|
Win32GetCurrentDirW(path, size);
|
|
#else // OS_WIN32
|
|
UnixGetCurrentDirW(path, size);
|
|
#endif // OS_WIN32
|
|
}
|
|
}
|
|
}
|
|
void GetHomeDir(char *path, UINT size)
|
|
{
|
|
// Validate arguments
|
|
if (path == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (GetEnv("HOME", path, size) == false)
|
|
{
|
|
char drive[MAX_SIZE];
|
|
char hpath[MAX_SIZE];
|
|
if (GetEnv("HOMEDRIVE", drive, sizeof(drive)) &&
|
|
GetEnv("HOMEPATH", hpath, sizeof(hpath)))
|
|
{
|
|
Format(path, sizeof(path), "%s%s", drive, hpath);
|
|
}
|
|
else
|
|
{
|
|
#ifdef OS_WIN32
|
|
Win32GetCurrentDir(path, size);
|
|
#else // OS_WIN32
|
|
UnixGetCurrentDir(path, size);
|
|
#endif // OS_WIN32
|
|
}
|
|
}
|
|
}
|
|
|
|
// Get the environment variable string
|
|
bool GetEnv(char *name, char *data, UINT size)
|
|
{
|
|
char *ret;
|
|
// Validate arguments
|
|
if (name == NULL || data == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
StrCpy(data, size, "");
|
|
|
|
ret = getenv(name);
|
|
if (ret == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
StrCpy(data, size, ret);
|
|
|
|
return true;
|
|
}
|
|
bool GetEnvW(wchar_t *name, wchar_t *data, UINT size)
|
|
{
|
|
#ifdef OS_WIN32
|
|
return GetEnvW_ForWin32(name, data, size);
|
|
#else // OS_WIN32
|
|
return GetEnvW_ForUnix(name, data, size);
|
|
#endif // OS_WIN32
|
|
}
|
|
|
|
#ifdef OS_WIN32
|
|
bool GetEnvW_ForWin32(wchar_t *name, wchar_t *data, UINT size)
|
|
{
|
|
wchar_t *ret;
|
|
// Validate arguments
|
|
if (name == NULL || data == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (IsNt() == false)
|
|
{
|
|
bool ret;
|
|
char *name_a = CopyUniToStr(name);
|
|
char data_a[MAX_SIZE];
|
|
|
|
ret = GetEnv(name_a, data_a, sizeof(data_a));
|
|
|
|
if (ret)
|
|
{
|
|
StrToUni(data, size, data_a);
|
|
}
|
|
|
|
Free(name_a);
|
|
|
|
return ret;
|
|
}
|
|
|
|
UniStrCpy(data, size, L"");
|
|
|
|
ret = _wgetenv(name);
|
|
if (ret == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
UniStrCpy(data, size, ret);
|
|
|
|
return true;
|
|
}
|
|
|
|
#endif // OS_WIN32
|
|
|
|
#ifdef OS_UNIX
|
|
|
|
bool GetEnvW_ForUnix(wchar_t *name, wchar_t *data, UINT size)
|
|
{
|
|
char *name_a;
|
|
bool ret;
|
|
char data_a[MAX_SIZE];
|
|
// Validate arguments
|
|
if (name == NULL || data == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
name_a = CopyUniToUtf(name);
|
|
|
|
ret = GetEnv(name_a, data_a, sizeof(data_a));
|
|
|
|
if (ret)
|
|
{
|
|
UtfToUni(data, size, data_a);
|
|
}
|
|
|
|
Free(name_a);
|
|
|
|
return ret;
|
|
}
|
|
|
|
#endif // OS_UNIX
|
|
|
|
// Get the memory information
|
|
void GetMemInfo(MEMINFO *info)
|
|
{
|
|
OSGetMemInfo(info);
|
|
}
|
|
|
|
// Start the single-instance
|
|
INSTANCE *NewSingleInstance(char *instance_name)
|
|
{
|
|
return NewSingleInstanceEx(instance_name, false);
|
|
}
|
|
INSTANCE *NewSingleInstanceEx(char *instance_name, bool user_local)
|
|
{
|
|
char name[MAX_SIZE];
|
|
INSTANCE *ret;
|
|
void *data;
|
|
|
|
if (instance_name != NULL)
|
|
{
|
|
if (user_local == false)
|
|
{
|
|
HashInstanceName(name, sizeof(name), instance_name);
|
|
}
|
|
else
|
|
{
|
|
HashInstanceNameLocal(name, sizeof(name), instance_name);
|
|
}
|
|
|
|
data = OSNewSingleInstance(name);
|
|
}
|
|
else
|
|
{
|
|
data = OSNewSingleInstance(NULL);
|
|
}
|
|
|
|
if (data == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
ret = ZeroMalloc(sizeof(INSTANCE));
|
|
if (instance_name != NULL)
|
|
{
|
|
ret->Name = CopyStr(instance_name);
|
|
}
|
|
|
|
ret->pData = data;
|
|
|
|
return ret;
|
|
}
|
|
|
|
// Release of single instance
|
|
void FreeSingleInstance(INSTANCE *inst)
|
|
{
|
|
// Validate arguments
|
|
if (inst == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
OSFreeSingleInstance(inst->pData);
|
|
|
|
if (inst->Name != NULL)
|
|
{
|
|
Free(inst->Name);
|
|
}
|
|
Free(inst);
|
|
}
|
|
|
|
// Hashing the instance name
|
|
void HashInstanceName(char *name, UINT size, char *instance_name)
|
|
{
|
|
char tmp[MAX_SIZE];
|
|
UCHAR hash[SHA1_SIZE];
|
|
char key[11];
|
|
// Validate arguments
|
|
if (name == NULL || instance_name == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
StrCpy(tmp, sizeof(tmp), instance_name);
|
|
Trim(tmp);
|
|
StrUpper(tmp);
|
|
|
|
Hash(hash, tmp, StrLen(tmp), SHA1_SIZE);
|
|
BinToStr(key, sizeof(key), hash, 5);
|
|
key[10] = 0;
|
|
|
|
Format(name, size, "VPN-%s", key);
|
|
|
|
if (OS_IS_WINDOWS_NT(GetOsInfo()->OsType))
|
|
{
|
|
if (GET_KETA(GetOsInfo()->OsType, 100) >= 2 ||
|
|
GetOsInfo()->OsType == OSTYPE_WINDOWS_NT_4_TERMINAL_SERVER)
|
|
{
|
|
StrCpy(tmp, sizeof(tmp), name);
|
|
Format(name, size, "Global\\%s", tmp);
|
|
}
|
|
}
|
|
}
|
|
void HashInstanceNameLocal(char *name, UINT size, char *instance_name)
|
|
{
|
|
char tmp[MAX_SIZE];
|
|
UCHAR hash[SHA1_SIZE];
|
|
char key[11];
|
|
// Validate arguments
|
|
if (name == NULL || instance_name == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
StrCpy(tmp, sizeof(tmp), instance_name);
|
|
Trim(tmp);
|
|
StrUpper(tmp);
|
|
|
|
Hash(hash, tmp, StrLen(tmp), SHA1_SIZE);
|
|
BinToStr(key, sizeof(key), hash, 5);
|
|
key[10] = 0;
|
|
|
|
Format(name, size, "VPN-%s", key);
|
|
|
|
if (OS_IS_WINDOWS_NT(GetOsInfo()->OsType))
|
|
{
|
|
if (GET_KETA(GetOsInfo()->OsType, 100) >= 2 ||
|
|
GetOsInfo()->OsType == OSTYPE_WINDOWS_NT_4_TERMINAL_SERVER)
|
|
{
|
|
StrCpy(tmp, sizeof(tmp), name);
|
|
Format(name, size, "Local\\%s", tmp);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Run the process
|
|
bool Run(char *filename, char *arg, bool hide, bool wait)
|
|
{
|
|
// Validate arguments
|
|
if (filename == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return OSRun(filename, arg, hide, wait);
|
|
}
|
|
bool RunW(wchar_t *filename, wchar_t *arg, bool hide, bool wait)
|
|
{
|
|
// Validate arguments
|
|
if (filename == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return OSRunW(filename, arg, hide, wait);
|
|
}
|
|
|
|
// Date and time related functions
|
|
void GetDateTimeStr64Uni(wchar_t *str, UINT size, UINT64 sec64)
|
|
{
|
|
char tmp[MAX_SIZE];
|
|
if (str == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
GetDateTimeStr64(tmp, sizeof(tmp), sec64);
|
|
StrToUni(str, size, tmp);
|
|
}
|
|
void GetDateTimeStr64(char *str, UINT size, UINT64 sec64)
|
|
{
|
|
SYSTEMTIME st;
|
|
UINT64ToSystem(&st, sec64);
|
|
GetDateTimeStr(str, size, &st);
|
|
}
|
|
void GetDateTimeStrMilli64(char *str, UINT size, UINT64 sec64)
|
|
{
|
|
SYSTEMTIME st;
|
|
UINT64ToSystem(&st, sec64);
|
|
GetDateTimeStrMilli(str, size, &st);
|
|
}
|
|
void GetDateTimeStrMilli64ForFileName(char *str, UINT size, UINT64 sec64)
|
|
{
|
|
SYSTEMTIME st;
|
|
UINT64ToSystem(&st, sec64);
|
|
GetDateTimeStrMilliForFileName(str, size, &st);
|
|
}
|
|
void GetDateTimeStrMilliForFileName(char *str, UINT size, SYSTEMTIME *tm)
|
|
{
|
|
Format(str, size, "%04u%02u%02u_%02u%02u%02u",
|
|
tm->wYear, tm->wMonth, tm->wDay, tm->wHour, tm->wMinute, tm->wSecond);
|
|
}
|
|
void GetDateStr64(char *str, UINT size, UINT64 sec64)
|
|
{
|
|
SYSTEMTIME st;
|
|
if (sec64 == 0)
|
|
{
|
|
StrCpy(str, size, "(Unknown)");
|
|
return;
|
|
}
|
|
UINT64ToSystem(&st, sec64);
|
|
GetDateStr(str, size, &st);
|
|
}
|
|
void GetDateTimeStrEx64(wchar_t *str, UINT size, UINT64 sec64, LOCALE *locale)
|
|
{
|
|
SYSTEMTIME st;
|
|
if (locale == NULL)
|
|
{
|
|
locale = ¤t_locale;
|
|
}
|
|
if (sec64 == 0 || SystemToLocal64(sec64) == 0 || LocalToSystem64(sec64) == 0)
|
|
{
|
|
UniStrCpy(str, size, locale->Unknown);
|
|
return;
|
|
}
|
|
UINT64ToSystem(&st, sec64);
|
|
GetDateTimeStrEx(str, size, &st, locale);
|
|
}
|
|
void GetTimeStrEx64(wchar_t *str, UINT size, UINT64 sec64, LOCALE *locale)
|
|
{
|
|
SYSTEMTIME st;
|
|
if (locale == NULL)
|
|
{
|
|
locale = ¤t_locale;
|
|
}
|
|
if (sec64 == 0 || SystemToLocal64(sec64) == 0 || LocalToSystem64(sec64) == 0)
|
|
{
|
|
UniStrCpy(str, size, locale->Unknown);
|
|
return;
|
|
}
|
|
UINT64ToSystem(&st, sec64);
|
|
GetTimeStrEx(str, size, &st, locale);
|
|
}
|
|
void GetDateStrEx64(wchar_t *str, UINT size, UINT64 sec64, LOCALE *locale)
|
|
{
|
|
SYSTEMTIME st;
|
|
if (locale == NULL)
|
|
{
|
|
locale = ¤t_locale;
|
|
}
|
|
if (sec64 == 0 || SystemToLocal64(sec64) == 0 || LocalToSystem64(sec64) == 0)
|
|
{
|
|
UniStrCpy(str, size, locale->Unknown);
|
|
return;
|
|
}
|
|
UINT64ToSystem(&st, sec64);
|
|
GetDateStrEx(str, size, &st, locale);
|
|
}
|
|
void GetTimeStrMilli64(char *str, UINT size, UINT64 sec64)
|
|
{
|
|
SYSTEMTIME st;
|
|
if (sec64 == 0 || SystemToLocal64(sec64) == 0 || LocalToSystem64(sec64) == 0)
|
|
{
|
|
StrCpy(str, size, "(Unknown)");
|
|
return;
|
|
}
|
|
UINT64ToSystem(&st, sec64);
|
|
GetTimeStrMilli(str, size, &st);
|
|
}
|
|
void GetTimeStr64(char *str, UINT size, UINT64 sec64)
|
|
{
|
|
SYSTEMTIME st;
|
|
if (sec64 == 0 || SystemToLocal64(sec64) == 0 || LocalToSystem64(sec64) == 0)
|
|
{
|
|
StrCpy(str, size, "(Unknown)");
|
|
return;
|
|
}
|
|
UINT64ToSystem(&st, sec64);
|
|
GetTimeStr(str, size, &st);
|
|
}
|
|
|
|
// Convert to a time to be used safely in the current POSIX implementation
|
|
UINT64 SafeTime64(UINT64 sec64)
|
|
{
|
|
return MAKESURE(sec64, 0, 4102243323123ULL);
|
|
}
|
|
|
|
// Thread pool
|
|
static SK *thread_pool = NULL;
|
|
static COUNTER *thread_count = NULL;
|
|
|
|
// Initialization of thread pool
|
|
void InitThreading()
|
|
{
|
|
thread_pool = NewSk();
|
|
thread_count = NewCounter();
|
|
}
|
|
|
|
// Release of thread pool
|
|
void FreeThreading()
|
|
{
|
|
while (true)
|
|
{
|
|
if (Count(thread_count) == 0)
|
|
{
|
|
break;
|
|
}
|
|
|
|
SleepThread(25);
|
|
}
|
|
|
|
while (true)
|
|
{
|
|
THREAD_POOL_DATA *pd;
|
|
THREAD *t = Pop(thread_pool);
|
|
|
|
if (t == NULL)
|
|
{
|
|
break;
|
|
}
|
|
|
|
pd = (THREAD_POOL_DATA *)t->param;
|
|
|
|
pd->ThreadProc = NULL;
|
|
Set(pd->Event);
|
|
|
|
WaitThreadInternal(t);
|
|
|
|
pd = (THREAD_POOL_DATA *)t->param;
|
|
ReleaseEvent(pd->Event);
|
|
ReleaseEvent(pd->InitFinishEvent);
|
|
|
|
ReleaseThreadInternal(t);
|
|
|
|
Free(pd);
|
|
}
|
|
|
|
ReleaseSk(thread_pool);
|
|
|
|
DeleteCounter(thread_count);
|
|
thread_count = NULL;
|
|
}
|
|
|
|
// Thread pool procedure
|
|
void ThreadPoolProc(THREAD *t, void *param)
|
|
{
|
|
THREAD_POOL_DATA *pd;
|
|
// Validate arguments
|
|
if (t == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
pd = (THREAD_POOL_DATA *)param;
|
|
|
|
NoticeThreadInitInternal(t);
|
|
|
|
while (true)
|
|
{
|
|
THREAD *thread;
|
|
UINT i, num;
|
|
EVENT **ee;
|
|
|
|
// Wait for the next job
|
|
Wait(pd->Event, INFINITE);
|
|
|
|
if (pd->ThreadProc == NULL)
|
|
{
|
|
// Stop the pool thread
|
|
break;
|
|
}
|
|
|
|
thread = pd->Thread;
|
|
thread->ThreadId = ThreadId();
|
|
|
|
// Initialization is completed
|
|
Set(pd->InitFinishEvent);
|
|
|
|
// Set the thread name
|
|
if (thread->Name != NULL)
|
|
{
|
|
SetThreadName(thread->ThreadId, thread->Name, thread->param);
|
|
}
|
|
else
|
|
{
|
|
SetThreadName(thread->ThreadId, "Unknown", 0);
|
|
}
|
|
|
|
// Run the thread procedure
|
|
pd->ThreadProc(pd->Thread, thread->param);
|
|
|
|
// Set the thread name
|
|
SetThreadName(thread->ThreadId, NULL, 0);
|
|
|
|
pd->Thread->Stopped = true;
|
|
|
|
thread->PoolHalting = true;
|
|
|
|
// Set the waiting event list
|
|
LockList(thread->PoolWaitList);
|
|
{
|
|
num = LIST_NUM(thread->PoolWaitList);
|
|
ee = ToArray(thread->PoolWaitList);
|
|
|
|
DeleteAll(thread->PoolWaitList);
|
|
}
|
|
UnlockList(thread->PoolWaitList);
|
|
|
|
for (i = 0;i < num;i++)
|
|
{
|
|
EVENT *e = ee[i];
|
|
|
|
Set(e);
|
|
ReleaseEvent(e);
|
|
}
|
|
|
|
Free(ee);
|
|
|
|
while (true)
|
|
{
|
|
if (Count(thread->ref->c) <= 1)
|
|
{
|
|
break;
|
|
}
|
|
|
|
Wait(thread->release_event, 256);
|
|
}
|
|
|
|
ReleaseThread(thread);
|
|
|
|
#ifdef OS_WIN32
|
|
// For Win32: Recover the priority of the thread
|
|
MsRestoreThreadPriority();
|
|
#endif // OS_WIN32
|
|
|
|
// Register the thread itself to the thread pool
|
|
LockSk(thread_pool);
|
|
{
|
|
Push(thread_pool, t);
|
|
}
|
|
UnlockSk(thread_pool);
|
|
|
|
Dec(thread_count);
|
|
}
|
|
}
|
|
|
|
// Set the thread name
|
|
void SetThreadName(UINT thread_id, char *name, void *param)
|
|
{
|
|
#ifdef OS_WIN32
|
|
if (IsDebug())
|
|
{
|
|
char tmp[MAX_SIZE];
|
|
|
|
if (name == NULL)
|
|
{
|
|
strcpy(tmp, "idle");
|
|
}
|
|
else
|
|
{
|
|
sprintf(tmp, "%s (0x%x)", name, (UINT)param);
|
|
}
|
|
|
|
Win32SetThreadName(thread_id, tmp);
|
|
}
|
|
#else // OS_WIN32
|
|
#ifdef _DEBUG
|
|
#ifdef PR_SET_NAME
|
|
char tmp[MAX_SIZE];
|
|
|
|
if (name == NULL)
|
|
{
|
|
strcpy(tmp, "idle");
|
|
}
|
|
else
|
|
{
|
|
sprintf(tmp, "%s (%p)", name, param);
|
|
}
|
|
|
|
tmp[15] = 0;
|
|
|
|
prctl(PR_SET_NAME, (unsigned long)tmp, 0, 0, 0);
|
|
#endif // PR_SET_NAME
|
|
#endif // _DEBUG
|
|
#endif // OS_WIN32
|
|
}
|
|
|
|
// Do Nothing
|
|
UINT DoNothing()
|
|
{
|
|
return g_zero;
|
|
}
|
|
|
|
// Thread creation (pool)
|
|
THREAD *NewThreadNamed(THREAD_PROC *thread_proc, void *param, char *name)
|
|
{
|
|
THREAD *host = NULL;
|
|
THREAD_POOL_DATA *pd = NULL;
|
|
THREAD *ret;
|
|
bool new_thread = false;
|
|
// Validate arguments
|
|
if (thread_proc == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
if (IsTrackingEnabled() == false)
|
|
{
|
|
DoNothing();
|
|
}
|
|
|
|
Inc(thread_count);
|
|
|
|
LockSk(thread_pool);
|
|
{
|
|
// Examine whether there is a thread that is currently vacant in the pool
|
|
host = Pop(thread_pool);
|
|
}
|
|
UnlockSk(thread_pool);
|
|
|
|
if (host == NULL)
|
|
{
|
|
// Create a new thread because a vacant thread is not found
|
|
pd = ZeroMalloc(sizeof(THREAD_POOL_DATA));
|
|
pd->Event = NewEvent();
|
|
pd->InitFinishEvent = NewEvent();
|
|
host = NewThreadInternal(ThreadPoolProc, pd);
|
|
WaitThreadInitInternal(host);
|
|
|
|
new_thread = true;
|
|
}
|
|
else
|
|
{
|
|
pd = (THREAD_POOL_DATA *)host->param;
|
|
}
|
|
|
|
// Creating a thread pool
|
|
ret = ZeroMalloc(sizeof(THREAD));
|
|
ret->ref = NewRef();
|
|
ret->thread_proc = thread_proc;
|
|
ret->param = param;
|
|
ret->pData = NULL;
|
|
ret->init_finished_event = NewEvent();
|
|
ret->PoolThread = true;
|
|
ret->PoolWaitList = NewList(NULL);
|
|
ret->PoolHostThread = host;
|
|
ret->release_event = NewEvent();
|
|
|
|
if (IsEmptyStr(name) == false)
|
|
{
|
|
ret->Name = CopyStr(name);
|
|
}
|
|
|
|
// Run
|
|
pd->ThreadProc = thread_proc;
|
|
pd->Thread = ret;
|
|
AddRef(ret->ref);
|
|
|
|
Set(pd->Event);
|
|
|
|
Wait(pd->InitFinishEvent, INFINITE);
|
|
|
|
current_num_thread++;
|
|
|
|
// Debug("current_num_thread = %u\n", current_num_thread);
|
|
|
|
return ret;
|
|
}
|
|
|
|
// Clean up of thread (pool)
|
|
void CleanupThread(THREAD *t)
|
|
{
|
|
// Validate arguments
|
|
if (t == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
ReleaseEvent(t->init_finished_event);
|
|
ReleaseEvent(t->release_event);
|
|
ReleaseList(t->PoolWaitList);
|
|
|
|
if (t->Name != NULL)
|
|
{
|
|
Free(t->Name);
|
|
}
|
|
|
|
Free(t);
|
|
|
|
current_num_thread--;
|
|
//Debug("current_num_thread = %u\n", current_num_thread);
|
|
}
|
|
|
|
// Release thread (pool)
|
|
void ReleaseThread(THREAD *t)
|
|
{
|
|
UINT ret;
|
|
EVENT *e;
|
|
// Validate arguments
|
|
if (t == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
e = t->release_event;
|
|
if (e != NULL)
|
|
{
|
|
AddRef(e->ref);
|
|
}
|
|
|
|
ret = Release(t->ref);
|
|
Set(e);
|
|
|
|
ReleaseEvent(e);
|
|
|
|
if (ret == 0)
|
|
{
|
|
CleanupThread(t);
|
|
}
|
|
}
|
|
|
|
// Notify the completion of the thread initialization (pool)
|
|
void NoticeThreadInit(THREAD *t)
|
|
{
|
|
// Validate arguments
|
|
if (t == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Notification
|
|
Set(t->init_finished_event);
|
|
}
|
|
|
|
// Wait the completion of the thread initialization (pool)
|
|
void WaitThreadInit(THREAD *t)
|
|
{
|
|
// Validate arguments
|
|
if (t == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// KS
|
|
KS_INC(KS_WAITFORTHREAD_COUNT);
|
|
|
|
// Wait
|
|
Wait(t->init_finished_event, INFINITE);
|
|
}
|
|
|
|
// Wait for the termination of the thread (pool)
|
|
bool WaitThread(THREAD *t, UINT timeout)
|
|
{
|
|
bool ret = false;
|
|
EVENT *e = NULL;
|
|
// Validate arguments
|
|
if (t == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
LockList(t->PoolWaitList);
|
|
{
|
|
if (t->PoolHalting)
|
|
{
|
|
// Has already been stopped
|
|
ret = true;
|
|
}
|
|
else
|
|
{
|
|
// Register the completion notifying event to the list
|
|
e = NewEvent();
|
|
AddRef(e->ref);
|
|
Insert(t->PoolWaitList, e);
|
|
}
|
|
}
|
|
UnlockList(t->PoolWaitList);
|
|
|
|
if (e != NULL)
|
|
{
|
|
// Wait Event
|
|
ret = Wait(e, timeout);
|
|
|
|
LockList(t->PoolWaitList);
|
|
{
|
|
if (Delete(t->PoolWaitList, e))
|
|
{
|
|
ReleaseEvent(e);
|
|
}
|
|
}
|
|
UnlockList(t->PoolWaitList);
|
|
|
|
ReleaseEvent(e);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
// Get Thread ID
|
|
UINT ThreadId()
|
|
{
|
|
return OSThreadId();
|
|
}
|
|
|
|
// Creating a thread
|
|
THREAD *NewThreadInternal(THREAD_PROC *thread_proc, void *param)
|
|
{
|
|
THREAD *t;
|
|
UINT retry = 0;
|
|
// Validate arguments
|
|
if (thread_proc == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
// Initialize Thread object
|
|
t = ZeroMalloc(sizeof(THREAD));
|
|
t->init_finished_event = NewEvent();
|
|
|
|
t->param = param;
|
|
t->ref = NewRef();
|
|
t->thread_proc = thread_proc;
|
|
|
|
// Wait until the OS to initialize the thread
|
|
while (true)
|
|
{
|
|
if ((retry++) > 60)
|
|
{
|
|
printf("\n\n*** error: new thread create failed.\n\n");
|
|
AbortExit();
|
|
}
|
|
if (OSInitThread(t))
|
|
{
|
|
break;
|
|
}
|
|
SleepThread(500);
|
|
}
|
|
|
|
// KS
|
|
KS_INC(KS_NEWTHREAD_COUNT);
|
|
|
|
return t;
|
|
}
|
|
|
|
// Release of thread
|
|
void ReleaseThreadInternal(THREAD *t)
|
|
{
|
|
// Validate arguments
|
|
if (t == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (Release(t->ref) == 0)
|
|
{
|
|
CleanupThreadInternal(t);
|
|
}
|
|
}
|
|
|
|
// Clean up of the thread
|
|
void CleanupThreadInternal(THREAD *t)
|
|
{
|
|
// Validate arguments
|
|
if (t == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Release of the thread
|
|
OSFreeThread(t);
|
|
|
|
// Release the event
|
|
ReleaseEvent(t->init_finished_event);
|
|
// Memory release
|
|
Free(t);
|
|
|
|
// KS
|
|
KS_INC(KS_FREETHREAD_COUNT);
|
|
}
|
|
|
|
// Wait for the termination of the thread
|
|
bool WaitThreadInternal(THREAD *t)
|
|
{
|
|
// Validate arguments
|
|
if (t == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return OSWaitThread(t);
|
|
}
|
|
|
|
// Notify that the thread initialization is complete
|
|
void NoticeThreadInitInternal(THREAD *t)
|
|
{
|
|
// Validate arguments
|
|
if (t == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Notify
|
|
Set(t->init_finished_event);
|
|
}
|
|
|
|
// Wait for completion of thread initialization
|
|
void WaitThreadInitInternal(THREAD *t)
|
|
{
|
|
// Validate arguments
|
|
if (t == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// KS
|
|
KS_INC(KS_WAITFORTHREAD_COUNT);
|
|
|
|
// Wait
|
|
Wait(t->init_finished_event, INFINITE);
|
|
}
|
|
|
|
// Get the date and time string by using the locale information
|
|
void GetDateTimeStrEx(wchar_t *str, UINT size, SYSTEMTIME *st, LOCALE *locale)
|
|
{
|
|
wchar_t tmp1[MAX_SIZE];
|
|
wchar_t tmp2[MAX_SIZE];
|
|
// Validate arguments
|
|
if (str == NULL || st == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
GetDateStrEx(tmp1, sizeof(tmp1), st, locale);
|
|
GetTimeStrEx(tmp2, sizeof(tmp2), st, locale);
|
|
UniFormat(str, size, L"%s %s", tmp1, tmp2);
|
|
}
|
|
|
|
// Get the time string by using the locale information
|
|
void GetTimeStrEx(wchar_t *str, UINT size, SYSTEMTIME *st, LOCALE *locale)
|
|
{
|
|
wchar_t *tag = L"%02u%s%02u%s%02u%s";
|
|
// Validate arguments
|
|
if (str == NULL || st == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (_GETLANG() == SE_LANG_JAPANESE || _GETLANG() == SE_LANG_CHINESE_ZH)
|
|
{
|
|
tag = L"%2u%s%2u%s%2u%s";
|
|
}
|
|
|
|
locale = (locale != NULL ? locale : ¤t_locale);
|
|
UniFormat(str, size,
|
|
tag,
|
|
st->wHour, locale->HourStr,
|
|
st->wMinute, locale->MinuteStr,
|
|
st->wSecond, locale->SecondStr);
|
|
}
|
|
|
|
// Get a date string by using the locale information
|
|
void GetDateStrEx(wchar_t *str, UINT size, SYSTEMTIME *st, LOCALE *locale)
|
|
{
|
|
wchar_t *tag = L"%04u%s%02u%s%02u%s (%s)";
|
|
// Validate arguments
|
|
if (str == NULL || st == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (_GETLANG() == SE_LANG_JAPANESE || _GETLANG() == SE_LANG_CHINESE_ZH)
|
|
{
|
|
tag = L"%4u%s%2u%s%2u%s(%s)";
|
|
}
|
|
|
|
locale = (locale != NULL ? locale : ¤t_locale);
|
|
UniFormat(str, size,
|
|
tag,
|
|
st->wYear, locale->YearStr,
|
|
st->wMonth, locale->MonthStr,
|
|
st->wDay, locale->DayStr,
|
|
locale->DayOfWeek[st->wDayOfWeek]);
|
|
}
|
|
|
|
// Get the time string to milliseconds (for example, 12:34:56.789)
|
|
void GetTimeStrMilli(char *str, UINT size, SYSTEMTIME *st)
|
|
{
|
|
// Validate arguments
|
|
if (st == NULL || str == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
Format(str, size, "%02u:%02u:%02u.%03u",
|
|
st->wHour, st->wMinute, st->wSecond, st->wMilliseconds);
|
|
}
|
|
|
|
// Get the time string (for example, 12:34:56)
|
|
void GetTimeStr(char *str, UINT size, SYSTEMTIME *st)
|
|
{
|
|
// Validate arguments
|
|
if (str == NULL || st == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
Format(str, size, "%02u:%02u:%02u",
|
|
st->wHour, st->wMinute, st->wSecond);
|
|
}
|
|
|
|
// Get the date string (example: 2004/07/23)
|
|
void GetDateStr(char *str, UINT size, SYSTEMTIME *st)
|
|
{
|
|
// Validate arguments
|
|
if (str == NULL || st == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
Format(str, size, "%04u-%02u-%02u",
|
|
st->wYear, st->wMonth, st->wDay);
|
|
}
|
|
|
|
// Get the date and time string (example: 2004/07/23 12:34:56)
|
|
void GetDateTimeStr(char *str, UINT size, SYSTEMTIME *st)
|
|
{
|
|
// Validate arguments
|
|
if (str == NULL || st == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
Format(str, size, "%04u-%02u-%02u %02u:%02u:%02u",
|
|
st->wYear, st->wMonth, st->wDay,
|
|
st->wHour, st->wMinute, st->wSecond);
|
|
}
|
|
|
|
// Get the date and time string in milliseconds (example: 2004/07/23 12:34:56.789)
|
|
void GetDateTimeStrMilli(char *str, UINT size, SYSTEMTIME *st)
|
|
{
|
|
// Validate arguments
|
|
if (str == NULL || st == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
Format(str, size, "%04u-%02u-%02u %02u:%02u:%02u.%03u",
|
|
st->wYear, st->wMonth, st->wDay,
|
|
st->wHour, st->wMinute, st->wSecond,
|
|
st->wMilliseconds);
|
|
}
|
|
|
|
// Get the date and time string in RFC3339 format (example: 2017-09-27T18:25:55.434-9:00)
|
|
void GetDateTimeStrRFC3339(char *str, UINT size, SYSTEMTIME *st, int timezone_min){
|
|
// Validate arguments
|
|
if (str == NULL || st == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if(timezone_min == 0){
|
|
Format(str, size, "%04u-%02u-%02uT%02u:%02u:%02u.%03uZ",
|
|
st->wYear, st->wMonth, st->wDay,
|
|
st->wHour, st->wMinute, st->wSecond,
|
|
st->wMilliseconds);
|
|
}else{
|
|
Format(str, size, "%04u-%02u-%02uT%02u:%02u:%02u.%03u%+02d:%02d",
|
|
st->wYear, st->wMonth, st->wDay,
|
|
st->wHour, st->wMinute, st->wSecond,
|
|
st->wMilliseconds, timezone_min/60, timezone_min%60);
|
|
}
|
|
}
|
|
|
|
// Get the time string
|
|
void GetSpanStr(char *str, UINT size, UINT64 sec64)
|
|
{
|
|
char tmp[MAX_SIZE];
|
|
// Validate arguments
|
|
if (str == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
StrCpy(tmp, sizeof(tmp), "");
|
|
if (sec64 >= (UINT64)(1000 * 3600 * 24))
|
|
{
|
|
Format(tmp, sizeof(tmp), "%u:", (UINT)(sec64 / (UINT64)(1000 * 3600 * 24)));
|
|
}
|
|
|
|
Format(tmp, sizeof(tmp), "%s%02u:%02u:%02u", tmp,
|
|
(UINT)(sec64 % (UINT64)(1000 * 60 * 60 * 24)) / (1000 * 60 * 60),
|
|
(UINT)(sec64 % (UINT64)(1000 * 60 * 60)) / (1000 * 60),
|
|
(UINT)(sec64 % (UINT64)(1000 * 60)) / 1000);
|
|
|
|
Trim(tmp);
|
|
StrCpy(str, size, tmp);
|
|
}
|
|
|
|
// Get the time string (in milliseconds)
|
|
void GetSpanStrMilli(char *str, UINT size, UINT64 sec64)
|
|
{
|
|
char tmp[MAX_SIZE];
|
|
// Validate arguments
|
|
if (str == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
StrCpy(tmp, sizeof(tmp), "");
|
|
if (sec64 >= (UINT64)(1000 * 3600 * 24))
|
|
{
|
|
Format(tmp, sizeof(tmp), "%u:", (UINT)(sec64 / (UINT64)(1000 * 3600 * 24)));
|
|
}
|
|
|
|
Format(tmp, sizeof(tmp), "%s%02u:%02u:%02u.%03u", tmp,
|
|
(UINT)(sec64 % (UINT64)(1000 * 60 * 60 * 24)) / (1000 * 60 * 60),
|
|
(UINT)(sec64 % (UINT64)(1000 * 60 * 60)) / (1000 * 60),
|
|
(UINT)(sec64 % (UINT64)(1000 * 60)) / 1000,
|
|
(UINT)(sec64 % (UINT64)(1000)));
|
|
|
|
Trim(tmp);
|
|
StrCpy(str, size, tmp);
|
|
}
|
|
|
|
// Get the time string (extended)
|
|
void GetSpanStrEx(wchar_t *str, UINT size, UINT64 sec64, LOCALE *locale)
|
|
{
|
|
wchar_t tmp[MAX_SIZE];
|
|
// Validate arguments
|
|
if (str == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
locale = (locale != NULL ? locale : ¤t_locale);
|
|
|
|
UniStrCpy(tmp, sizeof(tmp), L"");
|
|
if (sec64 >= (UINT64)(1000 * 3600 * 24))
|
|
{
|
|
UniFormat(tmp, sizeof(tmp), L"%u%s ", (UINT)(sec64 / (UINT64)(1000 * 3600 * 24)),
|
|
locale->SpanDay);
|
|
}
|
|
|
|
UniFormat(tmp, sizeof(tmp), L"%s%u%s %02u%s %02u%s", tmp,
|
|
(UINT)(sec64 % (UINT64)(1000 * 60 * 60 * 24)) / (1000 * 60 * 60),
|
|
locale->SpanHour,
|
|
(UINT)(sec64 % (UINT64)(1000 * 60 * 60)) / (1000 * 60),
|
|
locale->SpanMinute,
|
|
(UINT)(sec64 % (UINT64)(1000 * 60)) / 1000,
|
|
locale->SpanSecond);
|
|
|
|
UniTrim(tmp);
|
|
UniStrCpy(str, size, tmp);
|
|
}
|
|
|
|
// Get the current locale information
|
|
void GetCurrentLocale(LOCALE *locale)
|
|
{
|
|
// Validate arguments
|
|
if (locale == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
Copy(locale, ¤t_locale, sizeof(LOCALE));
|
|
}
|
|
|
|
// Set the locale information
|
|
void SetLocale(wchar_t *str)
|
|
{
|
|
wchar_t *set_locale_str;
|
|
LOCALE tmp;
|
|
|
|
if (str != NULL)
|
|
{
|
|
set_locale_str = str;
|
|
}
|
|
else
|
|
{
|
|
set_locale_str = default_locale_str;
|
|
}
|
|
|
|
if (LoadLocale(&tmp, set_locale_str) == false)
|
|
{
|
|
if (LoadLocale(&tmp, default_locale_str) == false)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
Copy(¤t_locale, &tmp, sizeof(LOCALE));
|
|
}
|
|
|
|
#define COPY_LOCALE_STR(dest, size, src) UniStrCpy(dest, size, UniStrCmp(src, L"$") == 0 ? L"" : src)
|
|
|
|
// Read the locale information
|
|
bool LoadLocale(LOCALE *locale, wchar_t *str)
|
|
{
|
|
UNI_TOKEN_LIST *tokens;
|
|
UINT i;
|
|
// Validate arguments
|
|
if (locale == NULL || str == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Analysis of the token
|
|
tokens = UniParseToken(str, L" ");
|
|
if (tokens->NumTokens != 18)
|
|
{
|
|
UniFreeToken(tokens);
|
|
return false;
|
|
}
|
|
|
|
// Set to the structure
|
|
Zero(locale, sizeof(LOCALE));
|
|
COPY_LOCALE_STR(locale->YearStr, sizeof(locale->YearStr), tokens->Token[0]);
|
|
COPY_LOCALE_STR(locale->MonthStr, sizeof(locale->MonthStr), tokens->Token[1]);
|
|
COPY_LOCALE_STR(locale->DayStr, sizeof(locale->DayStr), tokens->Token[2]);
|
|
COPY_LOCALE_STR(locale->HourStr, sizeof(locale->HourStr), tokens->Token[3]);
|
|
COPY_LOCALE_STR(locale->MinuteStr, sizeof(locale->MinuteStr), tokens->Token[4]);
|
|
COPY_LOCALE_STR(locale->SecondStr, sizeof(locale->SecondStr), tokens->Token[5]);
|
|
|
|
for (i = 0;i < 7;i++)
|
|
{
|
|
COPY_LOCALE_STR(locale->DayOfWeek[i], sizeof(locale->DayOfWeek[i]),
|
|
tokens->Token[6 + i]);
|
|
}
|
|
|
|
COPY_LOCALE_STR(locale->SpanDay, sizeof(locale->SpanDay), tokens->Token[13]);
|
|
COPY_LOCALE_STR(locale->SpanHour, sizeof(locale->SpanHour), tokens->Token[14]);
|
|
COPY_LOCALE_STR(locale->SpanMinute, sizeof(locale->SpanMinute), tokens->Token[15]);
|
|
COPY_LOCALE_STR(locale->SpanSecond, sizeof(locale->SpanSecond), tokens->Token[16]);
|
|
|
|
COPY_LOCALE_STR(locale->Unknown, sizeof(locale->Unknown), tokens->Token[17]);
|
|
|
|
UniFreeToken(tokens);
|
|
return true;
|
|
}
|
|
|
|
// Convert SYSTEMTIME into DOS date
|
|
USHORT SystemToDosDate(SYSTEMTIME *st)
|
|
{
|
|
return (USHORT)(
|
|
((UINT)(st->wYear - 1980) << 9) |
|
|
((UINT)st->wMonth<< 5) |
|
|
(UINT)st->wDay);
|
|
}
|
|
USHORT System64ToDosDate(UINT64 i)
|
|
{
|
|
SYSTEMTIME st;
|
|
UINT64ToSystem(&st, i);
|
|
return SystemToDosDate(&st);
|
|
}
|
|
|
|
// Convert SYSTEMTIME into DOS time
|
|
USHORT SystemToDosTime(SYSTEMTIME *st)
|
|
{
|
|
return (USHORT)(
|
|
((UINT)st->wHour << 11) |
|
|
((UINT)st->wMinute << 5) |
|
|
((UINT)st->wSecond >> 1));
|
|
}
|
|
USHORT System64ToDosTime(UINT64 i)
|
|
{
|
|
SYSTEMTIME st;
|
|
UINT64ToSystem(&st, i);
|
|
return SystemToDosTime(&st);
|
|
}
|
|
|
|
// Convert the tm to the SYSTEMTIME
|
|
void TmToSystem(SYSTEMTIME *st, struct tm *t)
|
|
{
|
|
struct tm tmp;
|
|
// Validate arguments
|
|
if (st == NULL || t == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
Copy(&tmp, t, sizeof(struct tm));
|
|
NormalizeTm(&tmp);
|
|
|
|
Zero(st, sizeof(SYSTEMTIME));
|
|
st->wYear = MAKESURE(tmp.tm_year + 1900, 1970, 2099);
|
|
st->wMonth = MAKESURE(tmp.tm_mon + 1, 1, 12);
|
|
st->wDay = MAKESURE(tmp.tm_mday, 1, 31);
|
|
st->wDayOfWeek = MAKESURE(tmp.tm_wday, 0, 6);
|
|
st->wHour = MAKESURE(tmp.tm_hour, 0, 23);
|
|
st->wMinute = MAKESURE(tmp.tm_min, 0, 59);
|
|
st->wSecond = MAKESURE(tmp.tm_sec, 0, 59);
|
|
st->wMilliseconds = 0;
|
|
}
|
|
|
|
// Convert the SYSTEMTIME to tm
|
|
void SystemToTm(struct tm *t, SYSTEMTIME *st)
|
|
{
|
|
// Validate arguments
|
|
if (t == NULL || st == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
Zero(t, sizeof(struct tm));
|
|
t->tm_year = MAKESURE(st->wYear, 1970, 2099) - 1900;
|
|
t->tm_mon = MAKESURE(st->wMonth, 1, 12) - 1;
|
|
t->tm_mday = MAKESURE(st->wDay, 1, 31);
|
|
t->tm_hour = MAKESURE(st->wHour, 0, 23);
|
|
t->tm_min = MAKESURE(st->wMinute, 0, 59);
|
|
t->tm_sec = MAKESURE(st->wSecond, 0, 59);
|
|
|
|
t->tm_isdst = -1;
|
|
NormalizeTm(t);
|
|
}
|
|
|
|
// Convert the time_t to SYSTEMTIME
|
|
void TimeToSystem(SYSTEMTIME *st, time_64t t)
|
|
{
|
|
struct tm tmp;
|
|
// Validate arguments
|
|
if (st == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
TimeToTm(&tmp, t);
|
|
TmToSystem(st, &tmp);
|
|
}
|
|
|
|
// Convert the time_t to 64-bit SYSTEMTIME
|
|
UINT64 TimeToSystem64(time_64t t)
|
|
{
|
|
SYSTEMTIME st;
|
|
|
|
TimeToSystem(&st, t);
|
|
|
|
return SystemToUINT64(&st);
|
|
}
|
|
|
|
// Convert the SYSTEMTIME to time_t
|
|
time_64t SystemToTime(SYSTEMTIME *st)
|
|
{
|
|
struct tm t;
|
|
// Validate arguments
|
|
if (st == NULL)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
SystemToTm(&t, st);
|
|
return TmToTime(&t);
|
|
}
|
|
|
|
// Convert a 64-bit SYSTEMTIME to a time_t
|
|
time_64t System64ToTime(UINT64 i)
|
|
{
|
|
SYSTEMTIME st;
|
|
|
|
UINT64ToSystem(&st, i);
|
|
|
|
return SystemToTime(&st);
|
|
}
|
|
|
|
// Convert the tm to time_t
|
|
time_64t TmToTime(struct tm *t)
|
|
{
|
|
time_64t tmp;
|
|
// Validate arguments
|
|
if (t == NULL)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
tmp = c_mkgmtime(t);
|
|
if (tmp == (time_64t)-1)
|
|
{
|
|
return 0;
|
|
}
|
|
return tmp;
|
|
}
|
|
|
|
// Convert time_t to tm
|
|
void TimeToTm(struct tm *t, time_64t time)
|
|
{
|
|
// Validate arguments
|
|
if (t == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
Zero(t, sizeof(struct tm));
|
|
c_gmtime_r(&time, t);
|
|
}
|
|
|
|
// Normalize the tm
|
|
void NormalizeTm(struct tm *t)
|
|
{
|
|
time_64t tmp;
|
|
// Validate arguments
|
|
if (t == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
tmp = c_mkgmtime(t);
|
|
if (tmp == (time_64t)-1)
|
|
{
|
|
return;
|
|
}
|
|
|
|
c_gmtime_r(&tmp, t);
|
|
}
|
|
|
|
// Normalize the SYSTEMTIME
|
|
void NormalizeSystem(SYSTEMTIME *st)
|
|
{
|
|
UINT64 sec64;
|
|
// Validate arguments
|
|
if (st == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
sec64 = SystemToUINT64(st);
|
|
UINT64ToSystem(st, sec64);
|
|
}
|
|
|
|
// Convert a 64-bit local time to a system time
|
|
UINT64 LocalToSystem64(UINT64 t)
|
|
{
|
|
SYSTEMTIME st;
|
|
UINT64ToSystem(&st, t);
|
|
LocalToSystem(&st, &st);
|
|
return SystemToUINT64(&st);
|
|
}
|
|
|
|
// Convert the 64bit system time to local time
|
|
UINT64 SystemToLocal64(UINT64 t)
|
|
{
|
|
SYSTEMTIME st;
|
|
UINT64ToSystem(&st, t);
|
|
SystemToLocal(&st, &st);
|
|
return SystemToUINT64(&st);
|
|
}
|
|
|
|
// Convert local time to system time
|
|
void LocalToSystem(SYSTEMTIME *system, SYSTEMTIME *local)
|
|
{
|
|
UINT64 sec64;
|
|
// Validate arguments
|
|
if (local == NULL || system == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
sec64 = (UINT64)((INT64)SystemToUINT64(local) - GetTimeDiffEx(local, true));
|
|
UINT64ToSystem(system, sec64);
|
|
}
|
|
|
|
// Convert the system time to local time
|
|
void SystemToLocal(SYSTEMTIME *local, SYSTEMTIME *system)
|
|
{
|
|
UINT64 sec64;
|
|
// Validate arguments
|
|
if (local == NULL || system == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
sec64 = (UINT64)((INT64)SystemToUINT64(system) + GetTimeDiffEx(system, false));
|
|
UINT64ToSystem(local, sec64);
|
|
}
|
|
|
|
// Get the time difference between the local time and the system time based on the specified time
|
|
INT64 GetTimeDiffEx(SYSTEMTIME *basetime, bool local_time)
|
|
{
|
|
time_t tmp;
|
|
struct tm t1, t2;
|
|
SYSTEMTIME snow;
|
|
struct tm now;
|
|
SYSTEMTIME s1, s2;
|
|
INT64 ret;
|
|
|
|
Copy(&snow, basetime, sizeof(SYSTEMTIME));
|
|
|
|
if (sizeof(time_t) == 4)
|
|
{
|
|
if (snow.wYear >= 2038)
|
|
{
|
|
// For old systems: avoid the 2038-year problem
|
|
snow.wYear = 2037;
|
|
}
|
|
}
|
|
|
|
SystemToTm(&now, &snow);
|
|
if (local_time == false)
|
|
{
|
|
tmp = (time_t)c_mkgmtime(&now);
|
|
}
|
|
else
|
|
{
|
|
tmp = mktime(&now);
|
|
}
|
|
|
|
if (tmp == (time_t)-1)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
#ifndef OS_UNIX
|
|
Copy(&t1, localtime(&tmp), sizeof(struct tm));
|
|
Copy(&t2, gmtime(&tmp), sizeof(struct tm));
|
|
#else // OS_UNIX
|
|
localtime_r(&tmp, &t1);
|
|
gmtime_r(&tmp, &t2);
|
|
#endif // OS_UNIX
|
|
|
|
TmToSystem(&s1, &t1);
|
|
TmToSystem(&s2, &t2);
|
|
|
|
ret = (INT)SystemToUINT64(&s1) - (INT)SystemToUINT64(&s2);
|
|
|
|
return ret;
|
|
}
|
|
|
|
// Convert UINT64 to the SYSTEMTIME
|
|
void UINT64ToSystem(SYSTEMTIME *st, UINT64 sec64)
|
|
{
|
|
UINT64 tmp64;
|
|
UINT sec, millisec;
|
|
time_64t time;
|
|
// Validate arguments
|
|
if (st == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
sec64 = SafeTime64(sec64 + 32400000ULL);
|
|
tmp64 = sec64 / (UINT64)1000;
|
|
millisec = (UINT)(sec64 - tmp64 * (UINT64)1000);
|
|
sec = (UINT)tmp64;
|
|
time = (time_64t)sec;
|
|
TimeToSystem(st, time);
|
|
st->wMilliseconds = (WORD)millisec;
|
|
}
|
|
|
|
// Convert the SYSTEMTIME to UINT64
|
|
UINT64 SystemToUINT64(SYSTEMTIME *st)
|
|
{
|
|
UINT64 sec64;
|
|
time_64t time;
|
|
// Validate arguments
|
|
if (st == NULL)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
time = SystemToTime(st);
|
|
sec64 = (UINT64)time * (UINT64)1000;
|
|
sec64 += st->wMilliseconds;
|
|
|
|
return sec64 - 32400000ULL;
|
|
}
|
|
|
|
// Get local time in UINT64
|
|
UINT64 LocalTime64()
|
|
{
|
|
SYSTEMTIME s;
|
|
LocalTime(&s);
|
|
return SystemToUINT64(&s);
|
|
}
|
|
|
|
// Get the system time in UINT64
|
|
UINT64 SystemTime64()
|
|
{
|
|
SYSTEMTIME s;
|
|
SystemTime(&s);
|
|
return SystemToUINT64(&s);
|
|
}
|
|
|
|
// Get local time
|
|
void LocalTime(SYSTEMTIME *st)
|
|
{
|
|
SYSTEMTIME tmp;
|
|
// Validate arguments
|
|
if (st == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
SystemTime(&tmp);
|
|
SystemToLocal(st, &tmp);
|
|
}
|
|
|
|
// Get the System Time
|
|
void SystemTime(SYSTEMTIME *st)
|
|
{
|
|
// Validate arguments
|
|
if (st == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
OSGetSystemTime(st);
|
|
|
|
// KS
|
|
KS_INC(KS_GETTIME_COUNT);
|
|
}
|
|
|
|
time_64t c_mkgmtime(struct tm *tm)
|
|
{
|
|
int years, months, days, hours, minutes, seconds;
|
|
|
|
years = tm->tm_year + 1900; /* year - 1900 -> year */
|
|
months = tm->tm_mon; /* 0..11 */
|
|
days = tm->tm_mday - 1; /* 1..31 -> 0..30 */
|
|
hours = tm->tm_hour; /* 0..23 */
|
|
minutes = tm->tm_min; /* 0..59 */
|
|
seconds = tm->tm_sec; /* 0..61 in ANSI C. */
|
|
|
|
ADJUST_TM(seconds, minutes, 60);
|
|
ADJUST_TM(minutes, hours, 60);
|
|
ADJUST_TM(hours, days, 24);
|
|
ADJUST_TM(months, years, 12);
|
|
if (days < 0)
|
|
do {
|
|
if (--months < 0) {
|
|
--years;
|
|
months = 11;
|
|
}
|
|
days += monthlen(months, years);
|
|
} while (days < 0);
|
|
else
|
|
while (days >= monthlen(months, years)) {
|
|
days -= monthlen(months, years);
|
|
if (++months >= 12) {
|
|
++years;
|
|
months = 0;
|
|
}
|
|
}
|
|
|
|
/* Restore adjusted values in tm structure */
|
|
tm->tm_year = years - 1900;
|
|
tm->tm_mon = months;
|
|
tm->tm_mday = days + 1;
|
|
tm->tm_hour = hours;
|
|
tm->tm_min = minutes;
|
|
tm->tm_sec = seconds;
|
|
|
|
/* Set `days' to the number of days into the year. */
|
|
days += ydays[months] + (months > 1 && leap (years));
|
|
tm->tm_yday = days;
|
|
|
|
/* Now calculate `days' to the number of days since Jan 1, 1970. */
|
|
days = (unsigned)days + 365 * (unsigned)(years - 1970) +
|
|
(unsigned)(nleap (years));
|
|
tm->tm_wday = ((unsigned)days + 4) % 7; /* Jan 1, 1970 was Thursday. */
|
|
tm->tm_isdst = 0;
|
|
|
|
if (years < 1970)
|
|
return (time_64t)-1;
|
|
|
|
#if (defined(TM_YEAR_MAX) && defined(TM_MON_MAX) && defined(TM_MDAY_MAX))
|
|
#if (defined(TM_HOUR_MAX) && defined(TM_MIN_MAX) && defined(TM_SEC_MAX))
|
|
if (years > TM_YEAR_MAX ||
|
|
(years == TM_YEAR_MAX &&
|
|
(tm->tm_yday > ydays[TM_MON_MAX] + (TM_MDAY_MAX - 1) +
|
|
(TM_MON_MAX > 1 && leap (TM_YEAR_MAX)) ||
|
|
(tm->tm_yday == ydays[TM_MON_MAX] + (TM_MDAY_MAX - 1) +
|
|
(TM_MON_MAX > 1 && leap (TM_YEAR_MAX)) &&
|
|
(hours > TM_HOUR_MAX ||
|
|
(hours == TM_HOUR_MAX &&
|
|
(minutes > TM_MIN_MAX ||
|
|
(minutes == TM_MIN_MAX && seconds > TM_SEC_MAX) )))))))
|
|
return (time_64t)-1;
|
|
#endif
|
|
#endif
|
|
|
|
return (time_64t)(86400L * (unsigned long)(unsigned)days +
|
|
3600L * (unsigned long)hours +
|
|
(unsigned long)(60 * minutes + seconds));
|
|
}
|
|
|
|
// Get the system timer
|
|
UINT Tick()
|
|
{
|
|
// KS
|
|
KS_INC(KS_GETTICK_COUNT);
|
|
return OSGetTick();
|
|
}
|
|
|
|
// Sleep thread
|
|
void SleepThread(UINT time)
|
|
{
|
|
// KS
|
|
KS_INC(KS_SLEEPTHREAD_COUNT);
|
|
|
|
OSSleep(time);
|
|
}
|
|
|
|
// Yield
|
|
void YieldCpu()
|
|
{
|
|
OSYield();
|
|
}
|
|
|
|
// Stop system (abnormal termination)
|
|
void AbortExit()
|
|
{
|
|
#ifdef OS_WIN32
|
|
_exit(1);
|
|
#else // OS_WIN32
|
|
|
|
#ifdef RLIMIT_CORE
|
|
UnixSetResourceLimit(RLIMIT_CORE, 0);
|
|
#endif // RLIMIT_CORE
|
|
|
|
abort();
|
|
#endif // OS_WIN32
|
|
}
|
|
|
|
|
|
void AbortExitEx(char *msg)
|
|
{
|
|
FILE *f;
|
|
// Validate arguments
|
|
if (msg == NULL)
|
|
{
|
|
msg = "Unknown Error";
|
|
}
|
|
|
|
f = fopen("abort_error_log.txt", "w");
|
|
if (f != NULL)
|
|
{
|
|
fwrite(msg, 1, strlen(msg), f);
|
|
fclose(f);
|
|
}
|
|
|
|
fputs("Fatal Error: ", stdout);
|
|
fputs(msg, stdout);
|
|
fputs("\r\n", stdout);
|
|
|
|
#ifdef OS_WIN32
|
|
_exit(1);
|
|
#else // OS_WIN32
|
|
|
|
#ifdef RLIMIT_CORE
|
|
UnixSetResourceLimit(RLIMIT_CORE, 0);
|
|
#endif // RLIMIT_CORE
|
|
|
|
abort();
|
|
#endif // OS_WIN32
|
|
}
|
|
|