// SoftEther VPN Source Code - Stable Edition Repository
// Mayaqua Kernel
// 
// SoftEther VPN Server, Client and Bridge are free software under the Apache License, Version 2.0.
// 
// Copyright (c) Daiyuu Nobori.
// Copyright (c) SoftEther VPN Project, University of Tsukuba, Japan.
// Copyright (c) SoftEther Corporation.
// Copyright (c) all contributors on SoftEther VPN project in GitHub.
// 
// All Rights Reserved.
// 
// http://www.softether.org/
// 
// Authors: Daiyuu Nobori
// 
// License: The Apache License, Version 2.0
// https://www.apache.org/licenses/LICENSE-2.0
// 
// DISCLAIMER
// ==========
// 
// 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.
// 
// 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.
// 
// READ AND UNDERSTAND THE 'WARNING.TXT' FILE BEFORE USING THIS SOFTWARE.
// SOME SOFTWARE PROGRAMS FROM THIRD PARTIES ARE INCLUDED ON THIS SOFTWARE WITH
// LICENSE CONDITIONS WHICH ARE DESCRIBED ON THE 'THIRD_PARTY.TXT' FILE.
// 
// 
// 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.


// Unix.c
// UNIX dependent code

#include <GlobalConst.h>

#ifdef	UNIX

#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>

#ifdef	UNIX_MACOS
#include <mach/clock.h>
#include <mach/mach.h>
#ifdef	NO_VLAN
// Struct statfs for MacOS X
typedef struct fsid { int32_t val[2]; } fsid_t;
struct statfs {
        short   f_otype;                /* TEMPORARY SHADOW COPY OF f_type */
        short   f_oflags;               /* TEMPORARY SHADOW COPY OF f_flags */
        long    f_bsize;                /* fundamental file system block size */
        long    f_iosize;               /* optimal transfer block size */
        long    f_blocks;               /* total data blocks in file system */
        long    f_bfree;                /* free blocks in fs */
        long    f_bavail;               /* free blocks avail to non-superuser */
        long    f_files;                /* total file nodes in file system */
        long    f_ffree;                /* free file nodes in fs */
        fsid_t  f_fsid;                 /* file system id */
        uid_t   f_owner;                /* user that mounted the filesystem */
        short   f_reserved1;    /* spare for later */
        short   f_type;                 /* type of filesystem */
    long        f_flags;                /* copy of mount exported flags */
        long    f_reserved2[2]; /* reserved for future use */
        char    f_fstypename[15]; /* fs type name */
        char    f_mntonname[90];  /* directory on which mounted */
        char    f_mntfromname[90];/* mounted filesystem */
};
#else	// NO_VLAN
#include <sys/mount.h>
#endif	// NO_VLAN
#endif	// UNIX_MACOS

// Scandir() function for Solaris
#ifdef	UNIX_SOLARIS
#define scandir local_scandir
#define	alphasort local_alphasort

int local_scandir(const char *dir, struct dirent ***namelist,
            int (*select)(const struct dirent *),
            int (*compar)(const struct dirent **, const struct dirent **))
{
  DIR *d;
  struct dirent *entry;
  register int i=0;
  size_t entrysize;

  if ((d=opendir(dir)) == NULL)
     return(-1);

  *namelist=NULL;
  while ((entry=readdir(d)) != NULL)
  {
    if (select == NULL || (select != NULL && (*select)(entry)))
    {
      *namelist=(struct dirent **)realloc((void *)(*namelist),
                 (size_t)((i+1)*sizeof(struct dirent *)));
	if (*namelist == NULL) return(-1);
	entrysize=sizeof(struct dirent)-sizeof(entry->d_name)+strlen(entry->d_name)+1;
	(*namelist)[i]=(struct dirent *)malloc(entrysize);
	if ((*namelist)[i] == NULL) return(-1);
	memcpy((*namelist)[i], entry, entrysize);
	i++;
    }
  }
  if (closedir(d)) return(-1);
  if (i == 0) return(-1);
  if (compar != NULL)
    qsort((void *)(*namelist), (size_t)i, sizeof(struct dirent *), compar);
    
  return(i);
}

int local_alphasort(const struct dirent **a, const struct dirent **b)
{
  return(strcmp((*a)->d_name, (*b)->d_name));
}


#endif	// UNIX_SOLARIS

// Thread data for UNIX
typedef struct UNIXTHREAD
{
	pthread_t thread;
	bool finished;
} UNIXTHREAD;

// Thread startup information for UNIX
typedef struct UNIXTHREADSTARTUPINFO
{
	THREAD_PROC *thread_proc;
	void *param;
	THREAD *thread;
} UNIXTHREADSTARTUPINFO;

// Thread function prototype for UNIX
void *UnixDefaultThreadProc(void *param);

// Current process ID
static pid_t current_process_id = 0;

// File I/O data for UNIX
typedef struct UNIXIO
{
	int fd;
	bool write_mode;
} UNIXIO;

// Lock file data for UNIX
typedef struct UNIXLOCKFILE
{
	char FileName[MAX_SIZE];
	int fd;
} UNIXLOCKFILE;

// Event data for UNIX
typedef struct UNIXEVENT
{
	pthread_mutex_t mutex;
	pthread_cond_t cond;
	bool signal;
} UNIXEVENT;

static pthread_mutex_t get_time_lock;
static pthread_mutex_t malloc_lock;
static bool high_process = false;

static bool unix_svc_terminate = false;
static int solaris_sleep_p1 = -1, solaris_sleep_p2 = -1;

// Create a dispatch table
OS_DISPATCH_TABLE *UnixGetDispatchTable()
{
	static OS_DISPATCH_TABLE t =
	{
		UnixInit,
		UnixFree,
		UnixMemoryAlloc,
		UnixMemoryReAlloc,
		UnixMemoryFree,
		UnixGetTick,
		UnixGetSystemTime,
		UnixInc32,
		UnixDec32,
		UnixSleep,
		UnixNewLock,
		UnixLock,
		UnixUnlock,
		UnixDeleteLock,
		UnixInitEvent,
		UnixSetEvent,
		UnixResetEvent,
		UnixWaitEvent,
		UnixFreeEvent,
		UnixWaitThread,
		UnixFreeThread,
		UnixInitThread,
		UnixThreadId,
		UnixFileOpen,
		UnixFileOpenW,
		UnixFileCreate,
		UnixFileCreateW,
		UnixFileWrite,
		UnixFileRead,
		UnixFileClose,
		UnixFileFlush,
		UnixFileSize,
		UnixFileSeek,
		UnixFileDelete,
		UnixFileDeleteW,
		UnixMakeDir,
		UnixMakeDirW,
		UnixDeleteDir,
		UnixDeleteDirW,
		UnixGetCallStack,
		UnixGetCallStackSymbolInfo,
		UnixFileRename,
		UnixFileRenameW,
		UnixRun,
		UnixRunW,
		UnixIsSupportedOs,
		UnixGetOsInfo,
		UnixAlert,
		UnixAlertW,
		UnixGetProductId,
		UnixSetHighPriority,
		UnixRestorePriority,
		UnixNewSingleInstance,
		UnixFreeSingleInstance,
		UnixGetMemInfo,
		UnixYield,
	};

	return &t;
}

static void *signal_received_for_ignore(int sig, siginfo_t *info, void *ucontext) 
{
	return NULL;
}

// Ignore the signal flew to the thread
void UnixIgnoreSignalForThread(int sig)
{
	struct sigaction sa;

	Zero(&sa, sizeof(sa));
	sa.sa_handler = NULL;
	sa.sa_sigaction = signal_received_for_ignore;
	sa.sa_flags = SA_SIGINFO;

	sigemptyset(&sa.sa_mask);

	sigaction(SIGUSR1, &sa, NULL);
}

// Disable the off-loading function of the specific Ethernet device
void UnixDisableInterfaceOffload(char *name)
{
#ifdef	UNIX_LINUX
	char tmp[MAX_SIZE];
	TOKEN_LIST *t;
	char *names = "rx tx sg tso ufo gso gro lro rxvlan txvlan ntuple rxhash";
	// Validate arguments
	if (name == NULL)
	{
		return;
	}

	t = ParseToken(names, " ");

	if (t != NULL)
	{
		UINT i;
		for (i = 0;i < t->NumTokens;i++)
		{
			char *a = t->Token[i];

			Format(tmp, sizeof(tmp), "ethtool -K %s %s off 2>/dev/null", name, a);
			FreeToken(UnixExec(tmp));
		}
	}

	FreeToken(t);
#endif	// UNIX_LINUX
}

// Validate whether the UNIX is running in a VM
bool UnixIsInVmMain()
{
	TOKEN_LIST *t = NULL;
	bool ret = false;
	char *vm_str_list = "Hypervisor detected,VMware Virtual Platform,VMware Virtual USB,qemu,xen,paravirtualized,virtual hd,virtualhd,virtual pc,virtualpc,kvm,oracle vm,oraclevm,parallels,xvm,bochs";

#ifdef	UNIX_LINUX
	t = UnixExec("/bin/dmesg");

	if (t != NULL)
	{
		BUF *b = NewBuf();
		UINT i;

		for (i = 0;i < t->NumTokens;i++)
		{
			char *line = t->Token[i];

			AddBufStr(b, line);
			AddBufStr(b, " ");
		}

		WriteBufInt(b, 0);

//		printf("%s\n", b->Buf);

		ret = InStrList(b->Buf, vm_str_list, ",", false);

		FreeBuf(b);
		FreeToken(t);
	}
#endif	// UNIX_LINUX

	return ret;
}
bool UnixIsInVm()
{
	static bool is_in_vm_flag = false;
	static bool is_in_vm_ret = false;

	if (is_in_vm_flag == false)
	{
		is_in_vm_ret = UnixIsInVmMain();
		is_in_vm_flag = true;
	}

	return is_in_vm_ret;
}

// Run quietly in the UNIX
void UnixExecSilent(char *cmd)
{
	char tmp[MAX_SIZE];
	// Validate arguments
	if (cmd == NULL)
	{
		return;
	}

	Format(tmp, sizeof(tmp), "%s 2>/dev/null", cmd);

	FreeToken(UnixExec(tmp));
}

// Enable / disable the ESP processing in the kernel
void UnixSetEnableKernelEspProcessing(bool b)
{
	if (GetOsInfo()->OsType == OSTYPE_MACOS_X)
	{
		// Mac OS X
		if (b)
		{
			UnixExecSilent("/usr/sbin/sysctl -w net.inet.ipsec.esp_port=4500");
		}
		else
		{
			UnixExecSilent("/usr/sbin/sysctl -w net.inet.ipsec.esp_port=4501");
		}
	}
}

// Run a command and return its result
TOKEN_LIST *UnixExec(char *cmd)
{
	FILE *fp;
	char tmp[MAX_SIZE];
	char *ptr;
	LIST *o;
	UINT i;
	TOKEN_LIST *ret;
	// Validate arguments
	if (cmd == NULL)
	{
		return NULL;
	}

	fp = popen(cmd, "r");
	if (fp == NULL)
	{
		return NULL;
	}

	o = NewList(NULL);

	while (true)
	{
		fgets(tmp, sizeof(tmp), fp);
		if (feof(fp))
		{
			break;
		}

		ptr = strchr(tmp, '\n');
		if (ptr != NULL)
		{
			*ptr = 0;
		}

		ptr = strchr(tmp, '\r');
		if (ptr != NULL)
		{
			*ptr = 0;
		}

		Add(o, CopyStr(tmp));
	}

	pclose(fp);

	ret = ListToTokenList(o);

	FreeStrList(o);

	return ret;
}

// Initialize the Sleep for Solaris
void UnixInitSolarisSleep()
{
	char tmp[MAX_SIZE];

	UnixNewPipe(&solaris_sleep_p1, &solaris_sleep_p2);
	read(solaris_sleep_p1, tmp, sizeof(tmp));
}

// Release the Sleep for Solaris
void UnixFreeSolarisSleep()
{
	UnixDeletePipe(solaris_sleep_p1, solaris_sleep_p2);
	solaris_sleep_p1 = -1;
	solaris_sleep_p2 = -1;
}

// Sleep for Solaris
void UnixSolarisSleep(UINT msec)
{
	struct pollfd p;

	memset(&p, 0, sizeof(p));
	p.fd = solaris_sleep_p1;
	p.events = POLLIN;

	poll(&p, 1, msec == INFINITE ? -1 : (int)msec);
}

// Get the free space of the disk
bool UnixGetDiskFreeW(wchar_t *path, UINT64 *free_size, UINT64 *used_size, UINT64 *total_size)
{
	char *path_a = CopyUniToStr(path);
	bool ret;

	ret = UnixGetDiskFree(path_a, free_size, used_size, total_size);

	Free(path_a);

	return ret;
}
bool UnixGetDiskFree(char *path, UINT64 *free_size, UINT64 *used_size, UINT64 *total_size)
{
	char tmp[MAX_PATH];
	bool ret = false;
	// Validate arguments
	if (path == NULL)
	{
		return false;
	}

	NormalizePath(tmp, sizeof(tmp), path);

	while ((ret = UnixGetDiskFreeMain(tmp, free_size, used_size, total_size)) == false)
	{
		if (StrCmpi(tmp, "/") == 0)
		{
			break;
		}

		GetDirNameFromFilePath(tmp, sizeof(tmp), tmp);
	}

	return ret;
}
bool UnixGetDiskFreeMain(char *path, UINT64 *free_size, UINT64 *used_size, UINT64 *total_size)
{
#ifndef	USE_STATVFS
	struct statfs st;
	char tmp[MAX_PATH];
	UINT64 v1 = 0, v2 = 0;
	bool ret = false;
	// Validate arguments
	if (path == NULL)
	{
		return false;
	}

	NormalizePath(tmp, sizeof(tmp), path);

	Zero(&st, sizeof(st));
	if (statfs(tmp, &st) == 0)
	{
		v1 = (UINT64)st.f_bsize * (UINT64)st.f_bavail;
		v2 = (UINT64)st.f_bsize * (UINT64)st.f_blocks;
		ret = true;
	}

	if (free_size != NULL)
	{
		*free_size = v1;
	}

	if (total_size != NULL)
	{
		*total_size = v2;
	}

	if (used_size != NULL)
	{
		*used_size = v2 - v1;
	}

	return ret;
#else	// USE_STATVFS
	struct statvfs st;
	char tmp[MAX_PATH];
	UINT64 v1 = 0, v2 = 0;
	bool ret = false;
	// Validate arguments
	if (path == NULL)
	{
		return false;
	}

	NormalizePath(tmp, sizeof(tmp), path);

	Zero(&st, sizeof(st));

	if (statvfs(tmp, &st) == 0)
	{
		v1 = (UINT64)st.f_bsize * (UINT64)st.f_bavail;
		v2 = (UINT64)st.f_bsize * (UINT64)st.f_blocks;
		ret = true;
	}

	if (free_size != NULL)
	{
		*free_size = v1;
	}

	if (total_size != NULL)
	{
		*total_size = v2;
	}

	if (used_size != NULL)
	{
		*used_size = v2 - v1;
	}

	return ret;
#endif	// USE_STATVFS
}

// Directory enumeration
DIRLIST *UnixEnumDirEx(char *dirname, COMPARE *compare)
{
	char tmp[MAX_PATH];
	DIRLIST *d;
	int n;
	struct dirent **e;
	LIST *o;
	// Validate arguments
	if (dirname == NULL)
	{
		return NULL;
	}

	o = NewListFast(compare);

	NormalizePath(tmp, sizeof(tmp), dirname);

	if (StrLen(tmp) >= 1 && tmp[StrLen(tmp) - 1] != '/')
	{
		StrCat(tmp, sizeof(tmp), "/");
	}

	e = NULL;
	n = scandir(tmp, &e, 0, alphasort);

	if (StrLen(tmp) >= 1 && tmp[StrLen(tmp) - 1] == '/')
	{
		tmp[StrLen(tmp) - 1] = 0;
	}

	if (n >= 0 && e != NULL)
	{
		UINT i;

		for (i = 0;i < (UINT)n;i++)
		{
			char *filename = e[i]->d_name;

			if (filename != NULL)
			{
				if (StrCmpi(filename, "..") != 0 && StrCmpi(filename, ".") != 0)
				{
					char fullpath[MAX_PATH];
					struct stat st;
					Format(fullpath, sizeof(fullpath), "%s/%s", tmp, filename);

					Zero(&st, sizeof(st));

					if (stat(fullpath, &st) == 0)
					{
						DIRENT *f = ZeroMalloc(sizeof(DIRENT));
						SYSTEMTIME t;

						f->Folder = S_ISDIR(st.st_mode) ? true : false;
						f->FileName = CopyStr(filename);
						f->FileNameW = CopyUtfToUni(f->FileName);

						Zero(&t, sizeof(t));
						TimeToSystem(&t, st.st_ctime);
						f->CreateDate = SystemToUINT64(&t);

						Zero(&t, sizeof(t));
						TimeToSystem(&t, st.st_mtime);
						f->UpdateDate = SystemToUINT64(&t);

						if (f->Folder == false)
						{
							f->FileSize = st.st_size;
						}

						Add(o, f);
					}
				}
			}

			free(e[i]);
		}

		free(e);
	}

	Sort(o);

	d = ZeroMalloc(sizeof(DIRLIST));
	d->NumFiles = LIST_NUM(o);
	d->File = ToArray(o);

	ReleaseList(o);

	return d;
}
DIRLIST *UnixEnumDirExW(wchar_t *dirname, COMPARE *compare)
{
	char *dirname_a = CopyUniToUtf(dirname);
	DIRLIST *ret;

	ret = UnixEnumDirEx(dirname_a, compare);

	Free(dirname_a);

	return ret;
}

// Check the execute permissions of the specified file
bool UnixCheckExecAccess(char *name)
{
	// Validate arguments
	if (name == NULL)
	{
		return false;
	}

	if (access(name, X_OK) == 0)
	{
		return true;
	}

	return false;
}
bool UnixCheckExecAccessW(wchar_t *name)
{
	char *name_a;
	bool ret;
	// Validate arguments
	if (name == NULL)
	{
		return false;
	}

	name_a = CopyUniToUtf(name);

	ret = UnixCheckExecAccess(name_a);

	Free(name_a);

	return ret;
}

// Raise the priority of the thread to highest
void UnixSetThreadPriorityRealtime()
{
	struct sched_param p;
	Zero(&p, sizeof(p));
	p.sched_priority = 255;
	pthread_setschedparam(pthread_self(), SCHED_RR, &p);
}

// Lower the priority of the thread
void UnixSetThreadPriorityLow()
{
	struct sched_param p;
	Zero(&p, sizeof(p));
	p.sched_priority = 32;
	pthread_setschedparam(pthread_self(), SCHED_OTHER, &p);
}

// Raise the priority of the thread
void UnixSetThreadPriorityHigh()
{
	struct sched_param p;
	Zero(&p, sizeof(p));
	p.sched_priority = 127;
	pthread_setschedparam(pthread_self(), SCHED_RR, &p);
}

// Set the priority of the thread to idle
void UnixSetThreadPriorityIdle()
{
	struct sched_param p;
	Zero(&p, sizeof(p));
	p.sched_priority = 1;
	pthread_setschedparam(pthread_self(), SCHED_OTHER, &p);
}

// Restore the priority of the thread to normal
void UnixRestoreThreadPriority()
{
	struct sched_param p;
	Zero(&p, sizeof(p));
	p.sched_priority = 64;
	pthread_setschedparam(pthread_self(), SCHED_OTHER, &p);
}

// Get the current directory
void UnixGetCurrentDir(char *dir, UINT size)
{
	// Validate arguments
	if (dir == NULL)
	{
		return;
	}

	getcwd(dir, size);
}
void UnixGetCurrentDirW(wchar_t *dir, UINT size)
{
	char dir_a[MAX_PATH];

	UnixGetCurrentDir(dir_a, sizeof(dir_a));

	UtfToUni(dir, size, dir_a);
}

// Yield
void UnixYield()
{
#ifdef UNIX_SOLARIS
	UnixSolarisSleep(1);
#else
	usleep(1000);
#endif
}

// Get the memory information
void UnixGetMemInfo(MEMINFO *info)
{
	// Validate arguments
	if (info == NULL)
	{
		return;
	}

	// I don't know!!
	Zero(info, sizeof(MEMINFO));
}

// Release of the single instance
void UnixFreeSingleInstance(void *data)
{
	UNIXLOCKFILE *o;
	struct flock lock;
	// Validate arguments
	if (data == NULL)
	{
		return;
	}

	o = (UNIXLOCKFILE *)data;

	Zero(&lock, sizeof(lock));
	lock.l_type = F_UNLCK;
	lock.l_whence = SEEK_SET;

	fcntl(o->fd, F_SETLK, &lock);
	close(o->fd);

	remove(o->FileName);

	Free(data);
}

// Creating a single instance
void *UnixNewSingleInstance(char *instance_name)
{
	UNIXLOCKFILE *ret;
	char tmp[MAX_SIZE];
	char name[MAX_SIZE];
	char dir[MAX_PATH];
	int fd;
	struct flock lock;
	int mode = S_IRUSR | S_IWUSR;
	// Validate arguments
	if (instance_name == NULL)
	{
		GetExeName(tmp, sizeof(tmp));
		HashInstanceName(tmp, sizeof(tmp), tmp);
	}
	else
	{
		StrCpy(tmp, sizeof(tmp), instance_name);
	}

	GetExeDir(dir, sizeof(dir));

	// File name generation
	Format(name, sizeof(name), "%s/.%s", dir, tmp);

	fd = open(name, O_WRONLY);
	if (fd == -1)
	{
		fd = creat(name, mode);
	}
	if (fd == -1)
	{
		Format(tmp, sizeof(tmp), "Unable to create %s.", name);
		Alert(tmp, NULL);
		exit(0);
		return NULL;
	}

	fchmod(fd, mode);
	chmod(name, mode);

	Zero(&lock, sizeof(lock));
	lock.l_type = F_WRLCK;
	lock.l_whence = SEEK_SET;

	if (fcntl(fd, F_SETLK, &lock) == -1)
	{
		close(fd);
		return NULL;
	}
	else
	{
		ret = ZeroMalloc(sizeof(UNIXLOCKFILE));
		ret->fd = fd;
		StrCpy(ret->FileName, sizeof(ret->FileName), name);
		return (void *)ret;
	}
}

// Set the high oom score
void UnixSetHighOomScore()
{
	IO *o;
	char tmp[256];

	sprintf(tmp, "/proc/%u/oom_score_adj", getpid());

	o = UnixFileCreate(tmp);
	if (o != NULL)
	{
		char tmp[128];
		sprintf(tmp, "%u\n", 800);
		UnixFileWrite(o, tmp, strlen(tmp));
		UnixFileClose(o, false);
	}
}

// Raise the priority of the process
void UnixSetHighPriority()
{
	if (high_process == false)
	{
		UINT pid = getpid();
		UINT pgid = getpgid(pid);

		high_process = true;
		nice(-20);

		setpriority(PRIO_PROCESS, pid, -20);
		setpriority(PRIO_PGRP, pgid, -20);
	}
}

// Restore the priority of the process
void UnixRestorePriority()
{
	if (high_process != false)
	{
		high_process = false;
		nice(20);
	}
}

UINT UnixGetNumberOfCpuInner()
{
	BUF *b;
	UINT ret = 0;

	b = ReadDump("/proc/cpuinfo");
	if (b != NULL)
	{
		while (true)
		{
			char *line = CfgReadNextLine(b);

			if (line == NULL)
			{
				break;
			}

			if (IsEmptyStr(line) == false)
			{
				TOKEN_LIST *t = ParseToken(line, ":");
				if (t != NULL)
				{
					if (t->NumTokens >= 2)
					{
						char *key = t->Token[0];
						char *value = t->Token[1];

						Trim(key);
						Trim(value);

						if (StrCmpi(key, "processor") == 0)
						{
							if (IsNum(value))
							{
								UINT i = ToInt(value) + 1;

								if (i >= 1 && i <= 128)
								{
									ret = MAX(ret, i);
								}
							}
						}
					}

					FreeToken(t);
				}
			}

			Free(line);
		}

		FreeBuf(b);
	}

	return ret;
}

// Get the product ID
char *UnixGetProductId()
{
	return CopyStr("--");
}

// Display an alert
void UnixAlertW(wchar_t *msg, wchar_t *caption)
{
	char *msg8 = CopyUniToUtf(msg);
	char *caption8 = CopyUniToUtf(caption);

	UnixAlert(msg8, caption8);

	Free(msg8);
	Free(caption8);
}
void UnixAlert(char *msg, char *caption)
{
	char *tag =
		"-- Alert: %s --\n%s\n";
	// Validate arguments
	if (msg == NULL)
	{
		msg = "Alert";
	}
	if (caption == NULL)
	{
		caption = CEDAR_PRODUCT_STR " VPN Kernel";
	}

	printf(tag, caption, msg);
}

// Get the information of the current OS
void UnixGetOsInfo(OS_INFO *info)
{
	// Validate arguments
	if (info == NULL)
	{
		return;
	}

	Zero(info, sizeof(OS_INFO));
	info->OsType = OSTYPE_UNIX_UNKNOWN;

#ifdef	UNIX_SOLARIS
	info->OsType = OSTYPE_SOLARIS;
#endif	// UNIX_SOLARIS

#ifdef	UNIX_CYGWIN
	info->OsType = OSTYPE_CYGWIN;
#endif	// UNIX_CYGWIN

#ifdef	UNIX_MACOS
	info->OsType = OSTYPE_MACOS_X;
#endif	// UNIX_MACOS

#ifdef	UNIX_BSD
	info->OsType = OSTYPE_BSD;
#endif	// UNIX_BSD

#ifdef	UNIX_LINUX
	info->OsType = OSTYPE_LINUX;
#endif	// UNIX_LINUX

	info->OsServicePack = 0;

	if (info->OsType != OSTYPE_LINUX)
	{
		info->OsSystemName = CopyStr("UNIX");
		info->OsProductName = CopyStr("UNIX");
	}
	else
	{
		info->OsSystemName = CopyStr("Linux");
		info->OsProductName = CopyStr("Linux");
	}

	if (info->OsType == OSTYPE_LINUX)
	{
		// Get the distribution name on Linux
		BUF *b;
		b = ReadDump("/etc/redhat-release");
		if (b != NULL)
		{
			info->OsVersion = CfgReadNextLine(b);
			info->OsVendorName = CopyStr("Red Hat, Inc.");
			FreeBuf(b);
		}
		else
		{
			b = ReadDump("/etc/turbolinux-release");
			if (b != NULL)
			{
				info->OsVersion = CfgReadNextLine(b);
				info->OsVendorName = CopyStr("Turbolinux, Inc.");
				FreeBuf(b);
			}
			else
			{
				info->OsVersion = CopyStr("Unknown Linux Version");
				info->OsVendorName = CopyStr("Unknown Vendor");
			}
		}

		info->KernelName = CopyStr("Linux Kernel");

		b = ReadDump("/proc/sys/kernel/osrelease");
		if (b != NULL)
		{
			info->KernelVersion = CfgReadNextLine(b);
			FreeBuf(b);
		}
		else
		{
			info->KernelVersion = CopyStr("Unknown Version");
		}
	}
	else
	{
		// In other cases
		Free(info->OsProductName);
		info->OsProductName = CopyStr(OsTypeToStr(info->OsType));
		info->OsVersion = CopyStr("Unknown Version");
		info->KernelName = CopyStr(OsTypeToStr(info->OsType));
		info->KernelVersion = CopyStr("Unknown Version");
	}
}

// Examine whether the current OS is supported by the PacketiX VPN Kernel
bool UnixIsSupportedOs()
{
	// Support all UNIX OS which can run PacketiX VPN
	return true;
}

// Run a specified command
bool UnixRunW(wchar_t *filename, wchar_t *arg, bool hide, bool wait)
{
	char *filename8 = CopyUniToUtf(filename);
	char *arg8 = CopyUniToUtf(arg);
	bool ret = UnixRun(filename8, arg8, hide, wait);

	Free(filename8);
	Free(arg8);

	return ret;
}
bool UnixRun(char *filename, char *arg, bool hide, bool wait)
{
	TOKEN_LIST *t;
	UINT ret;
	// Validate arguments
	if (filename == NULL)
	{
		return false;
	}
	if (arg == NULL)
	{
		arg = "";
	}

	// Create a child process
	ret = fork();
	if (ret == -1)
	{
		// Child process creation failure
		return false;
	}

	if (ret == 0)
	{
		Print("", filename, arg);
		// Child process
		if (hide)
		{
			// Close the standard I/O
			UnixCloseIO();
		}

		t = ParseToken(arg, " ");
		if (t == NULL)
		{
			AbortExit();
		}
		else
		{
			char **args;
			UINT num_args;
			UINT i;
			num_args = t->NumTokens + 2;
			args = ZeroMalloc(sizeof(char *) * num_args);
			args[0] = filename;
			for (i = 1;i < num_args - 1;i++)
			{
				args[i] = t->Token[i - 1];
			}
			execvp(filename, args);
			AbortExit();
		}
	}
	else
	{
		// Parent process
		pid_t pid = (pid_t)ret;

		if (wait)
		{
			int status = 0;
			// Wait for the termination of the child process
			if (waitpid(pid, &status, 0) == -1)
			{
				return false;
			}

			if (WEXITSTATUS(status) == 0)
			{
				return true;
			}
			else
			{
				return false;
			}
		}

		return true;
	}
}

// Initialize the daemon
void UnixDaemon(bool debug_mode)
{
	UINT ret;

	if (debug_mode)
	{
		// Debug mode
		signal(SIGHUP, SIG_IGN);
		return;
	}

	ret = fork();

	if (ret == -1)
	{
		// Error
		return;
	}
	else if (ret == 0)
	{
		// Create a new session for the child process
		setsid();

		// Close the standard I/O
		UnixCloseIO();

		// Mute the unwanted signal
		signal(SIGHUP, SIG_IGN);
	}
	else
	{
		// Terminate the parent process
		exit(0);
	}
}

// Close the standard I/O
void UnixCloseIO()
{
	static bool close_io_first = false;

	// Execute only once
	if (close_io_first)
	{
		return;
	}
	else
	{
		close(0);
		close(1);
		close(2);
		open("/dev/null", O_RDWR);
		dup2(0, 1);
		dup2(0, 2);
		close_io_first = false;
	}
}

// Change the file name
bool UnixFileRenameW(wchar_t *old_name, wchar_t *new_name)
{
	char *old_name8 = CopyUniToUtf(old_name);
	char *new_name8 = CopyUniToUtf(new_name);
	bool ret = UnixFileRename(old_name8, new_name8);

	Free(old_name8);
	Free(new_name8);

	return ret;
}
bool UnixFileRename(char *old_name, char *new_name)
{
	// Validate arguments
	if (old_name == NULL || new_name == NULL)
	{
		return false;
	}

	if (rename(old_name, new_name) != 0)
	{
		return false;
	}

	return true;
}

// Get the call stack
CALLSTACK_DATA *UnixGetCallStack()
{
	// This is not supported on non-Win32
	return NULL;
}

// Get the symbol information from the call stack
bool UnixGetCallStackSymbolInfo(CALLSTACK_DATA *s)
{
	// This is not supported on non-Win32
	return false;
}

// Delete the directory
bool UnixDeleteDirW(wchar_t *name)
{
	char *name8 = CopyUniToUtf(name);
	bool ret = UnixDeleteDir(name8);

	Free(name8);

	return ret;
}
bool UnixDeleteDir(char *name)
{
	// Validate arguments
	if (name == NULL)
	{
		return false;
	}

	if (rmdir(name) != 0)
	{
		return false;
	}

	return true;
}

// Create a directory
bool UnixMakeDirW(wchar_t *name)
{
	char *name8 = CopyUniToUtf(name);
	bool ret = UnixMakeDir(name8);

	Free(name8);

	return ret;
}
bool UnixMakeDir(char *name)
{
	// Validate arguments
	if (name == NULL)
	{
		return false;
	}

	if (mkdir(name, 0700) != 0)
	{
		return false;
	}

	return true;
}

// Delete the file
bool UnixFileDeleteW(wchar_t *name)
{
	bool ret;
	char *name8 = CopyUniToUtf(name);

	ret = UnixFileDelete(name8);

	Free(name8);

	return ret;
}
bool UnixFileDelete(char *name)
{
	// Validate arguments
	if (name == NULL)
	{
		return false;
	}

	if (remove(name) != 0)
	{
		return false;
	}

	return true;
}

// Seek the file
bool UnixFileSeek(void *pData, UINT mode, int offset)
{
	UNIXIO *p;
	UINT ret;
	// Validate arguments
	if (pData == NULL)
	{
		return 0;
	}
	if (mode != FILE_BEGIN && mode != FILE_END && mode != FILE_CURRENT)
	{
		return false;
	}

	p = (UNIXIO *)pData;

	ret = lseek(p->fd, offset, mode);

	if (ret == -1)
	{
		return false;
	}

	return true;
}

// Get the file size
UINT64 UnixFileSize(void *pData)
{
	struct stat st;
	UNIXIO *p;
	int r;
	// Validate arguments
	if (pData == NULL)
	{
		return 0;
	}

	p = (UNIXIO *)pData;

	Zero(&st, sizeof(st));
	r = fstat(p->fd, &st);
	if (r != 0)
	{
		return 0;
	}

	return (UINT64)st.st_size;
}

// Write to the file
bool UnixFileWrite(void *pData, void *buf, UINT size)
{
	UNIXIO *p;
	UINT ret;
	// Validate arguments
	if (pData == NULL || buf == NULL || size == 0)
	{
		return false;
	}

	p = (UNIXIO *)pData;

	ret = write(p->fd, buf, size);
	if (ret != size)
	{
		return false;
	}

	return true;
}

// Read from the file
bool UnixFileRead(void *pData, void *buf, UINT size)
{
	UNIXIO *p;
	UINT ret;
	// Validate arguments
	if (pData == NULL || buf == NULL || size == 0)
	{
		return false;
	}

	p = (UNIXIO *)pData;

	ret = read(p->fd, buf, size);
	if (ret != size)
	{
		return false;
	}

	return true;
}

// Flush to the file
void UnixFileFlush(void *pData)
{
	UNIXIO *p;
	bool write_mode;
	// Validate arguments
	if (pData == NULL)
	{
		return;
	}

	p = (UNIXIO *)pData;

	write_mode = p->write_mode;

	if (write_mode)
	{
		fsync(p->fd);
	}
}

// Close the file
void UnixFileClose(void *pData, bool no_flush)
{
	UNIXIO *p;
	bool write_mode;
	// Validate arguments
	if (pData == NULL)
	{
		return;
	}

	p = (UNIXIO *)pData;

	write_mode = p->write_mode;

	if (write_mode && no_flush == false)
	{
		fsync(p->fd);
	}

	close(p->fd);

	UnixMemoryFree(p);

	if (write_mode)
	{
		//sync();
	}
}

// Create a file
void *UnixFileCreateW(wchar_t *name)
{
	void *ret;
	char *name8 = CopyUniToUtf(name);

	ret = UnixFileCreate(name8);

	Free(name8);

	return ret;
}
void *UnixFileCreate(char *name)
{
	UNIXIO *p;
	int fd;
	// Validate arguments
	if (name == NULL)
	{
		return NULL;
	}

	fd = creat(name, 0600);
	if (fd == -1)
	{
		return NULL;
	}

	// Memory allocation
	p = UnixMemoryAlloc(sizeof(UNIXIO));
	p->fd = fd;
	p->write_mode = true;

	return (void *)p;
}

// Open the file
void *UnixFileOpenW(wchar_t *name, bool write_mode, bool read_lock)
{
	char *name8 = CopyUniToUtf(name);
	void *ret;

	ret = UnixFileOpen(name8, write_mode, read_lock);

	Free(name8);

	return ret;
}
void *UnixFileOpen(char *name, bool write_mode, bool read_lock)
{
	UNIXIO *p;
	int fd;
	int mode;
	// Validate arguments
	if (name == NULL)
	{
		return NULL;
	}

	if (write_mode == false)
	{
		mode = O_RDONLY;
	}
	else
	{
		mode = O_RDWR;
	}

	// Open the file
	fd = open(name, mode);
	if (fd == -1)
	{
		return NULL;
	}

	// Memory allocation
	p = UnixMemoryAlloc(sizeof(UNIXIO));
	p->fd = fd;
	p->write_mode = write_mode;

	return (void *)p;
}

// Return the current thread ID
UINT UnixThreadId()
{
	UINT ret;

	ret = (UINT)pthread_self();

	return ret;
}

// Thread function
void *UnixDefaultThreadProc(void *param)
{
	UNIXTHREAD *ut;
	UNIXTHREADSTARTUPINFO *info = (UNIXTHREADSTARTUPINFO *)param;
	if (info == NULL)
	{
		return 0;
	}

	ut = (UNIXTHREAD *)info->thread->pData;

	// Call the thread function
	info->thread_proc(info->thread, info->param);

	// Set a termination flag
	ut->finished = true;

	// Release of reference
	ReleaseThread(info->thread);

	UnixMemoryFree(info);

	FreeOpenSSLThreadState();

	return 0;
}

// Release of thread
void UnixFreeThread(THREAD *t)
{
	// Validate arguments
	if (t == NULL)
	{
		return;
	}

	// Free memory
	UnixMemoryFree(t->pData);
}

// Wait for the termination of the thread
bool UnixWaitThread(THREAD *t)
{
	UNIXTHREAD *ut;
	void *retcode = NULL;
	// Validate arguments
	if (t == NULL)
	{
		return false;
	}
	ut = (UNIXTHREAD *)t->pData;
	if (ut == NULL)
	{
		return false;
	}

	pthread_join(ut->thread, &retcode);

	return true;
}

// Thread initialization
bool UnixInitThread(THREAD *t)
{
	UNIXTHREAD *ut;
	UNIXTHREADSTARTUPINFO *info;
	pthread_attr_t attr;
	// Validate arguments
	if (t == NULL || t->thread_proc == NULL)
	{
		return false;
	}

	// Thread data creation
	ut = UnixMemoryAlloc(sizeof(UNIXTHREAD));
	Zero(ut, sizeof(UNIXTHREAD));

	// Creating the startup information
	info = UnixMemoryAlloc(sizeof(UNIXTHREADSTARTUPINFO));
	Zero(info, sizeof(UNIXTHREADSTARTUPINFO));
	info->param = t->param;
	info->thread_proc = t->thread_proc;
	info->thread = t;
	AddRef(t->ref);

	// Thread creation
	pthread_attr_init(&attr);
	pthread_attr_setstacksize(&attr, UNIX_THREAD_STACK_SIZE);

	t->pData = (void *)ut;

	if (pthread_create(&ut->thread, &attr, UnixDefaultThreadProc, info) != 0)
	{
		// An error has occured
		t->pData = NULL;
		Release(t->ref);
		UnixMemoryFree(ut);
		UnixMemoryFree(info);
		pthread_attr_destroy(&attr);
		return false;
	}

	pthread_attr_destroy(&attr);

	return true;
}

// Release the event
void UnixFreeEvent(EVENT *event)
{
	UNIXEVENT *ue = (UNIXEVENT *)event->pData;
	if (ue == NULL)
	{
		return;
	}

	pthread_cond_destroy(&ue->cond);
	pthread_mutex_destroy(&ue->mutex);

	UnixMemoryFree(ue);
}

// Wait for a event
bool UnixWaitEvent(EVENT *event, UINT timeout)
{
	UNIXEVENT *ue = (UNIXEVENT *)event->pData;
	struct timeval now;
	struct timespec to;
	bool ret;
	if (ue == NULL)
	{
		return false;
	}

	pthread_mutex_lock(&ue->mutex);
	gettimeofday(&now, NULL);
	to.tv_sec = now.tv_sec + timeout / 1000;
	to.tv_nsec = now.tv_usec * 1000 + (timeout % 1000) * 1000 * 1000;
	if ((to.tv_nsec / 1000000000) >= 1)
	{
		to.tv_sec += to.tv_nsec / 1000000000;
		to.tv_nsec = to.tv_nsec % 1000000000;
	}

	ret = true;

	while (ue->signal == false)
	{
		if (timeout != INFINITE)
		{
			if (pthread_cond_timedwait(&ue->cond, &ue->mutex, &to))
			{
				ret = false;
				break;
			}
		}
		else
		{
			pthread_cond_wait(&ue->cond, &ue->mutex);
		}
	}
	ue->signal = false;

	pthread_mutex_unlock(&ue->mutex);

	return ret;
}

// Reset the event
void UnixResetEvent(EVENT *event)
{
	UNIXEVENT *ue = (UNIXEVENT *)event->pData;
	if (ue == NULL)
	{
		return;
	}

	pthread_mutex_lock(&ue->mutex);
	ue->signal = false;
	pthread_cond_signal(&ue->cond);
	pthread_mutex_unlock(&ue->mutex);
}

// Set the event
void UnixSetEvent(EVENT *event)
{
	UNIXEVENT *ue = (UNIXEVENT *)event->pData;
	if (ue == NULL)
	{
		return;
	}

	pthread_mutex_lock(&ue->mutex);
	ue->signal = true;
	pthread_cond_signal(&ue->cond);
	pthread_mutex_unlock(&ue->mutex);
}

// Initialize the event
void UnixInitEvent(EVENT *event)
{
	UNIXEVENT *ue = UnixMemoryAlloc(sizeof(UNIXEVENT));

	Zero(ue, sizeof(UNIXEVENT));

	pthread_cond_init(&ue->cond, NULL);
	pthread_mutex_init(&ue->mutex, NULL);
	ue->signal = false;

	event->pData = (void *)ue;
}

// Delete the lock
void UnixDeleteLock(LOCK *lock)
{
	pthread_mutex_t *mutex;
	// Reset Ready flag safely
	UnixLock(lock);
	lock->Ready = false;
	UnixUnlockEx(lock, true);

	// Delete the mutex
	mutex = (pthread_mutex_t *)lock->pData;
	pthread_mutex_destroy(mutex);

	// Memory release
	UnixMemoryFree(mutex);
	UnixMemoryFree(lock);
}

// Unlock
void UnixUnlock(LOCK *lock)
{
	UnixUnlockEx(lock, false);
}
void UnixUnlockEx(LOCK *lock, bool inner)
{
	pthread_mutex_t *mutex;
	if (lock->Ready == false && inner == false)
	{
		// State is invalid
		return;
	}
	mutex = (pthread_mutex_t *)lock->pData;

	if ((--lock->locked_count) > 0)
	{
		return;
	}

	lock->thread_id = INFINITE;

	pthread_mutex_unlock(mutex);

	return;
}

// Lock
bool UnixLock(LOCK *lock)
{
	pthread_mutex_t *mutex;
	UINT thread_id = UnixThreadId();
	if (lock->Ready == false)
	{
		// State is invalid
		return false;
	}

	if (lock->thread_id == thread_id)
	{
		lock->locked_count++;
		return true;
	}

	mutex = (pthread_mutex_t *)lock->pData;

	pthread_mutex_lock(mutex);

	lock->thread_id = thread_id;
	lock->locked_count++;

	return true;
}

// Creating a new lock
LOCK *UnixNewLock()
{
	pthread_mutex_t *mutex;
	// Memory allocation
	LOCK *lock = UnixMemoryAlloc(sizeof(LOCK));

	// Create a mutex
	mutex = UnixMemoryAlloc(sizeof(pthread_mutex_t));

	// Initialization of the mutex
	pthread_mutex_init(mutex, NULL);

	lock->pData = (void *)mutex;
	lock->Ready = true;

	lock->thread_id = INFINITE;
	lock->locked_count = 0;

	return lock;
}

// Sleep
void UnixSleep(UINT time)
{
	UINT sec = 0, millisec = 0;
	// Validate arguments
	if (time == 0)
	{
		return;
	}

	if (time == INFINITE)
	{
		// Wait forever
		while (true)
		{
#ifdef UNIX_SOLARIS
			UnixSolarisSleep(time);
#else
			sleep(1000000);
#endif
		}
	}

#ifdef UNIX_SOLARIS
	UnixSolarisSleep(time);
#else

	// Prevent overflow
	sec = time / 1000;
	millisec = time % 1000;

	if (sec != 0)
	{
		sleep(sec);
	}
	if (millisec != 0)
	{
		usleep(millisec * 1000);
	}
#endif
}

// Decrement
void UnixDec32(UINT *value)
{
	if (value != NULL)
	{
		(*value)--;
	}
}

// Increment
void UnixInc32(UINT *value)
{
	if (value != NULL)
	{
		(*value)++;
	}
}

// Get the System Time
void UnixGetSystemTime(SYSTEMTIME *system_time)
{
	time_t now = 0;
	time_64t now2 = 0;
	struct tm tm;
	struct timeval tv;
	struct timezone tz;
	// Validate arguments
	if (system_time == NULL)
	{
		return;
	}

	pthread_mutex_lock(&get_time_lock);

	Zero(system_time, sizeof(SYSTEMTIME));
	Zero(&tv, sizeof(tv));
	Zero(&tz, sizeof(tz));

	time(&now);

	if (sizeof(time_t) == 4)
	{
		now2 = (time_64t)((UINT64)((UINT32)now));
	}
	else
	{
		now2 = now;
	}

	c_gmtime_r(&now2, &tm);

	TmToSystem(system_time, &tm);

	gettimeofday(&tv, &tz);

	system_time->wMilliseconds = tv.tv_usec / 1000;

	pthread_mutex_unlock(&get_time_lock);
}

// Get the system timer (64bit)
UINT64 UnixGetTick64()
{
#if	defined(OS_WIN32) || defined(CLOCK_REALTIME) || defined(CLOCK_MONOTONIC) || defined(CLOCK_HIGHRES)

	struct timespec t;
	UINT64 ret;
	static bool akirame = false;

	if (akirame)
	{
		return TickRealtimeManual();
	}

	Zero(&t, sizeof(t));

	// Function to get the boot time of the system
	// Be careful. The Implementation is depend on the system.
#ifdef	CLOCK_HIGHRES
	clock_gettime(CLOCK_HIGHRES, &t);
#else	// CLOCK_HIGHRES
#ifdef	CLOCK_MONOTONIC
	clock_gettime(CLOCK_MONOTONIC, &t);
#else	// CLOCK_MONOTONIC
	clock_gettime(CLOCK_REALTIME, &t);
#endif	// CLOCK_MONOTONIC
#endif	// CLOCK_HIGHRES

	ret = ((UINT64)((UINT32)t.tv_sec)) * 1000LL + (UINT64)t.tv_nsec / 1000000LL;

	if (akirame == false && ret == 0)
	{
		ret = TickRealtimeManual();
		akirame = true;
	}

	return ret;

#else
#ifdef	UNIX_MACOS
	static clock_serv_t clock_serv = 0;
	mach_timespec_t t;
	UINT64 ret;
	if (clock_serv == 0) {
		host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &clock_serv);
	}
	clock_get_time(clock_serv, &t);
	ret = ((UINT64)((UINT32)t.tv_sec)) * 1000LL + (UINT64)t.tv_nsec / 1000000LL;
	return ret;
#else
	return TickRealtimeManual();
#endif

#endif
}

// Get the system timer
UINT UnixGetTick()
{
	return (UINT)UnixGetTick64();
}

// Memory allocation
void *UnixMemoryAlloc(UINT size)
{
	void *r;
	pthread_mutex_lock(&malloc_lock);
	r = malloc(size);
	pthread_mutex_unlock(&malloc_lock);
	return r;
}

// Reallocation of the memory
void *UnixMemoryReAlloc(void *addr, UINT size)
{
	void *r;
	pthread_mutex_lock(&malloc_lock);
	r = realloc(addr, size);
	pthread_mutex_unlock(&malloc_lock);
	return r;
}

// Free the memory
void UnixMemoryFree(void *addr)
{
	pthread_mutex_lock(&malloc_lock);
	free(addr);
	pthread_mutex_unlock(&malloc_lock);
}

// SIGCHLD handler
void UnixSigChldHandler(int sig)
{
	// Recall the zombie processes
	while (waitpid(-1, NULL, WNOHANG) > 0);
	signal(SIGCHLD, UnixSigChldHandler);
}

// Disable core dump
void UnixDisableCoreDump()
{
#ifdef	RLIMIT_CORE
	UnixSetResourceLimit(RLIMIT_CORE, 0);
#endif	// RLIMIT_CORE
}

// Initialize the library for UNIX
void UnixInit()
{
	UNIXIO *o;
	UINT64 max_memory = UNIX_MAX_MEMORY;

	if (UnixIs64BitRlimSupported())
	{
		max_memory = UNIX_MAX_MEMORY_64;
	}

	UnixInitSolarisSleep();

	// Global lock
	pthread_mutex_init(&get_time_lock, NULL);
	pthread_mutex_init(&malloc_lock, NULL);

	// Get the Process ID
	current_process_id = getpid();

#ifdef	RLIMIT_CORE
	UnixSetResourceLimit(RLIMIT_CORE, max_memory);
#endif	// RLIMIT_CORE

#ifdef	RLIMIT_DATA
	UnixSetResourceLimit(RLIMIT_DATA, max_memory);
#endif	// RLIMIT_DATA

#ifdef	RLIMIT_NOFILE
#ifndef	UNIX_MACOS
	UnixSetResourceLimit(RLIMIT_NOFILE, UNIX_MAX_FD);
#else	// UNIX_MACOS
	UnixSetResourceLimit(RLIMIT_NOFILE, UNIX_MAX_FD_MACOS);
#endif	// UNIX_MACOS
#endif	// RLIMIT_NOFILE

#ifdef	RLIMIT_STACK
//	UnixSetResourceLimit(RLIMIT_STACK, max_memory);
#endif	// RLIMIT_STACK

#ifdef	RLIMIT_RSS
	UnixSetResourceLimit(RLIMIT_RSS, max_memory);
#endif	// RLIMIT_RSS

#ifdef	RLIMIT_LOCKS
	UnixSetResourceLimit(RLIMIT_LOCKS, UNIX_MAX_LOCKS);
#endif	// RLIMIT_LOCKS

#ifdef	RLIMIT_MEMLOCK
	UnixSetResourceLimit(RLIMIT_MEMLOCK, max_memory);
#endif	// RLIMIT_MEMLOCK

#ifdef	RLIMIT_NPROC
	UnixSetResourceLimit(RLIMIT_NPROC, UNIX_MAX_CHILD_PROCESSES);
#endif	// RLIMIT_NPROC

	// Write a value to the threads-max of the proc file system
	o = UnixFileCreate("/proc/sys/kernel/threads-max");
	if (o != NULL)
	{
		char tmp[128];
		sprintf(tmp, "%u\n", UNIX_LINUX_MAX_THREADS);
		UnixFileWrite(o, tmp, strlen(tmp));
		UnixFileClose(o, false);
	}

	// Set the signals that is to be ignored
	signal(SIGPIPE, SIG_IGN);
	signal(SIGALRM, SIG_IGN);

#ifdef	UNIX_BSD
	signal(64, SIG_IGN);
#endif	// UNIX_BSD

#ifdef	SIGXFSZ
	signal(SIGXFSZ, SIG_IGN);
#endif	// SIGXFSZ

	// Set a signal handler to salvage the child processes
	signal(SIGCHLD, UnixSigChldHandler);
}

// Release the library for UNIX
void UnixFree()
{
	UnixFreeSolarisSleep();

	current_process_id = 0;

	pthread_mutex_destroy(&get_time_lock);
}

// Adjust the upper limit of resources that may be occupied
void UnixSetResourceLimit(UINT id, UINT64 value)
{
	struct rlimit t;
	UINT64 hard_limit;

	if (UnixIs64BitRlimSupported() == false)
	{
		if (value > (UINT64)4294967295ULL)
		{
			value = (UINT64)4294967295ULL;
		}
	}

	Zero(&t, sizeof(t));
	getrlimit(id, &t);

	hard_limit = (UINT64)t.rlim_max;

	Zero(&t, sizeof(t));
	t.rlim_cur = (rlim_t)MIN(value, hard_limit);
	t.rlim_max = (rlim_t)hard_limit;
	setrlimit(id, &t);

	Zero(&t, sizeof(t));
	t.rlim_cur = (rlim_t)value;
	t.rlim_max = (rlim_t)value;
	setrlimit(id, &t);
}

// Is the rlim_t type 64-bit?
bool UnixIs64BitRlimSupported()
{
	if (sizeof(rlim_t) >= 8)
	{
		return true;
	}

	return false;
}

// Generate the PID file name
void UnixGenPidFileName(char *name, UINT size)
{
	char exe_name[MAX_PATH];
	UCHAR hash[MD5_SIZE];
	char tmp1[64];
	char dir[MAX_PATH];
	// Validate arguments
	if (name == NULL)
	{
		return;
	}

	GetExeDir(dir, sizeof(dir));

	GetExeName(exe_name, sizeof(exe_name));
	StrCat(exe_name, sizeof(exe_name), ":pid_hash");
	StrUpper(exe_name);

	Hash(hash, exe_name, StrLen(exe_name), false);
	BinToStr(tmp1, sizeof(tmp1), hash, sizeof(hash));

	Format(name, size, "%s/.pid_%s", dir, tmp1);
}

// Delete the PID file
void UnixDeletePidFile()
{
	char tmp[MAX_PATH];

	UnixGenPidFileName(tmp, sizeof(tmp));

	UnixFileDelete(tmp);
}

// Delete the CTL file
void UnixDeleteCtlFile()
{
	char tmp[MAX_PATH];

	UnixGenCtlFileName(tmp, sizeof(tmp));

	UnixFileDelete(tmp);
}

// Generate the CTL file name
void UnixGenCtlFileName(char *name, UINT size)
{
	char exe_name[MAX_PATH];
	UCHAR hash[MD5_SIZE];
	char tmp1[64];
	char dir[MAX_PATH];
	// Validate arguments
	if (name == NULL)
	{
		return;
	}

	GetExeDir(dir, sizeof(dir));

	GetExeName(exe_name, sizeof(exe_name));
	StrCat(exe_name, sizeof(exe_name), ":pid_hash");
	StrUpper(exe_name);

	Hash(hash, exe_name, StrLen(exe_name), false);
	BinToStr(tmp1, sizeof(tmp1), hash, sizeof(hash));

	Format(name, size, "%s/.ctl_%s", dir, tmp1);
}

// Write the CTL file
void UnixWriteCtlFile(UINT i)
{
	char tmp[MAX_PATH];
	char tmp2[64];
	IO *o;

	UnixGenCtlFileName(tmp, sizeof(tmp));
	Format(tmp2, sizeof(tmp2), "%u\n", i);

	o = FileCreate(tmp);
	if (o != NULL)
	{
		FileWrite(o, tmp2, StrLen(tmp2));
		FileClose(o);
	}
}

// Write to the PID file
void UnixWritePidFile(UINT pid)
{
	char tmp[MAX_PATH];
	char tmp2[64];
	IO *o;

	UnixGenPidFileName(tmp, sizeof(tmp));
	Format(tmp2, sizeof(tmp2), "%u\n", pid);

	o = FileCreate(tmp);
	if (o != NULL)
	{
		FileWrite(o, tmp2, StrLen(tmp2));
		FileClose(o);
	}
}

// Read the PID file
UINT UnixReadPidFile()
{
	char tmp[MAX_PATH];
	BUF *buf;

	UnixGenPidFileName(tmp, sizeof(tmp));

	buf = ReadDump(tmp);
	if (buf == NULL)
	{
		return 0;
	}

	Zero(tmp, sizeof(tmp));
	Copy(tmp, buf->Buf, MIN(buf->Size, sizeof(tmp)));
	FreeBuf(buf);

	return ToInt(tmp);
}

// Read the CTL file
UINT UnixReadCtlFile()
{
	char tmp[MAX_PATH];
	BUF *buf;

	UnixGenCtlFileName(tmp, sizeof(tmp));

	buf = ReadDump(tmp);
	if (buf == NULL)
	{
		return 0;
	}

	Zero(tmp, sizeof(tmp));
	Copy(tmp, buf->Buf, MIN(buf->Size, sizeof(tmp)));
	FreeBuf(buf);

	return ToInt(tmp);
}

// Get the UID
UINT UnixGetUID()
{
	return (UINT)getuid();
}

void UnixPrintVpnServerUrlInfo()
{
	IP ip;

	TryGetCurrentAcceptingIPv4Address(&ip);

	Print("\nLet's get started by accessing to the following URL from your PC:\n\n"
		"https://%r:%u/\n"
		"  or\n"
		"https://%r/\n\n"
		"Note: IP address may vary. Specify your server's IP address.\n"
		"A TLS certificate warning will appear because the server uses self signed certificate by default. That is natural. Continue with ignoring the TLS warning."
		"\n\n",
		&ip, GC_DEFAULT_PORT, &ip
		);
}

// Start the service
void UnixStartService(char *name)
{
	char *svc_name, *svc_title;
	char tmp[128];
	INSTANCE *inst;
	char exe[MAX_PATH];
	// Validate arguments
	if (name == NULL)
	{
		return;
	}

	GetExeName(exe, sizeof(exe));

	Format(tmp, sizeof(tmp), SVC_NAME, name);
	svc_name = _SS(tmp);
	Format(tmp, sizeof(tmp), SVC_TITLE, name);
	svc_title = _SS(tmp);

	// Examine whether the service has not been started already
	inst = NewSingleInstance(NULL);
	if (inst == NULL)
	{
		// Service is already running
		UniPrint(_UU("UNIX_SVC_ALREADY_START"), svc_title, svc_name);
	}
	else
	{
		int pid;
		// Begin to start the service
		UniPrint(_UU("UNIX_SVC_STARTED"), svc_title);

		if (UnixGetUID() != 0)
		{
			// Non-root warning
			UniPrint(_UU("UNIX_SVC_NONROOT"));
		}

		if (StrCmpi(name, "vpnserver") == 0 || StrCmpi(name, "vpnbridge") == 0)
		{
			// Print the IP address information
			UnixPrintVpnServerUrlInfo();
		}

		FreeSingleInstance(inst);

		// Create a child process
		pid = fork();
		if (pid == -1)
		{
			UniPrint(_UU("UNIX_SVC_ERROR_FORK"), svc_title);
		}
		else
		{
			if (pid == 0)
			{
				// Child process
				char *param = UNIX_SVC_ARG_EXEC_SVC;
				char **args;

				// Daemonize
				setsid();
				UnixCloseIO();
				signal(SIGHUP, SIG_IGN);

				// Prepare arguments
				args = ZeroMalloc(sizeof(char *) * 3);
				args[0] = exe;
				args[1] = param;
				args[2] = NULL;

				execvp(exe, args);
				AbortExit();
			}
			else
			{
				// Don't write the child process number to the file
//				UnixWritePidFile(pid);
			}
		}
	}
}

// Stop the Service
void UnixStopService(char *name)
{
	char *svc_name, *svc_title;
	char tmp[128];
	INSTANCE *inst;
	char exe[MAX_PATH];
	UINT pid;
	// Validate arguments
	if (name == NULL)
	{
		return;
	}

	GetExeName(exe, sizeof(exe));

	Format(tmp, sizeof(tmp), SVC_NAME, name);
	svc_name = _SS(tmp);
	Format(tmp, sizeof(tmp), SVC_TITLE, name);
	svc_title = _SS(tmp);

	inst = NewSingleInstance(NULL);
	pid = UnixReadPidFile();
	if (inst != NULL || pid == 0)
	{
		// Service is not running yet
		UniPrint(_UU("UNIX_SVC_NOT_STARTED"), svc_title, svc_name);
	}
	else
	{
		int status;

		// Stop the service
		UniPrint(_UU("UNIX_SVC_STOPPING"), svc_title);

		// Terminate the process
		kill(pid, SIGTERM);
#ifdef	UNIX_BSD
		UnixWriteCtlFile(Rand32());
#endif	// UNIX_BSD
		if (UnixWaitProcessEx(pid, UNIX_SERVICE_STOP_TIMEOUT_2))
		{
			UniPrint(_UU("UNIX_SVC_STOPPED"), svc_title);
		}
		else
		{
			// SIGKILL
			char tmp[256];

			Format(tmp, sizeof(tmp), "killall -KILL %s", name);

			UniPrint(_UU("UNIX_SVC_STOP_FAILED"), svc_title);
			system(tmp);
		}
	}

	FreeSingleInstance(inst);
}

// Handler of the stop signal to the process
void UnixSigTermHandler(int signum)
{
	if (signum == SIGTERM)
	{
		unix_svc_terminate = true;
	}
}

// The thread for stop service
void UnixStopThread(THREAD *t, void *param)
{
	SERVICE_FUNCTION *stop = (SERVICE_FUNCTION *)param;
	// Validate arguments
	if (t == NULL || param == NULL)
	{
		return;
	}

	stop();
}

// Execute the main body of the service
void UnixExecService(char *name, SERVICE_FUNCTION *start, SERVICE_FUNCTION *stop)
{
	char *svc_name, *svc_title;
	char tmp[128];
	INSTANCE *inst;
	UINT yobi_size = 1024 * 128;
	void *yobi1, *yobi2;
	UINT saved_ctl;
	// Validate arguments
	if (start == NULL || stop == NULL || name == NULL)
	{
		return;
	}

	Format(tmp, sizeof(tmp), SVC_NAME, name);
	svc_name = _SS(tmp);
	Format(tmp, sizeof(tmp), SVC_TITLE, name);
	svc_title = _SS(tmp);

	UnixWriteCtlFile(Rand32());
	saved_ctl = UnixReadCtlFile();

	inst = NewSingleInstance(NULL);
	if (inst != NULL)
	{
		THREAD *t;

		yobi1 = ZeroMalloc(yobi_size);
		yobi2 = ZeroMalloc(yobi_size);

		// Start
		UnixWritePidFile(getpid());

		start();

		// Starting complete. wait for arriving SIGTERM from another process
		signal(SIGTERM, &UnixSigTermHandler);
		while (unix_svc_terminate == false)
		{
#if	!(defined(UNIX_BSD) || defined(UNIX_MACOS))
			pause();
#else	// defined(UNIX_BSD) || defined(UNIX_MACOS)
			if (UnixReadCtlFile() != saved_ctl)
			{
				break;
			}

			SleepThread(1394);
#endif	// defined(UNIX_BSD) || defined(UNIX_MACOS)
		}

		// Stop
		Free(yobi1);
		t = NewThread(UnixStopThread, stop);
		if (t == NULL || (WaitThread(t, UNIX_SERVICE_STOP_TIMEOUT_1) == false))
		{
			// Terminate forcibly if creation of a halting thread have
			// failed or timed out
			Free(yobi2);
			FreeSingleInstance(inst);
			UnixDeletePidFile();
			_exit(0);
		}
		ReleaseThread(t);

		// Delete the PID file
		UnixDeletePidFile();

		// Delete the CTL file
		UnixDeleteCtlFile();

		FreeSingleInstance(inst);

		Free(yobi2);
	}
}

// Get whether the process with the specified pid exists
bool UnixIsProcess(UINT pid)
{
	if (getsid((pid_t)pid) == -1)
	{
		return false;
	}

	return true;
}

// Wait for the termination of the specified process
bool UnixWaitProcessEx(UINT pid,  UINT timeout)
{
	UINT64 start_tick = Tick64();
	UINT64 end_tick = start_tick + (UINT64)timeout;
	if (timeout == INFINITE)
	{
		end_tick = 0;
	}
	while (UnixIsProcess(pid))
	{
		if (end_tick != 0)
		{
			if (end_tick < Tick64())
			{
				return false;
			}
		}
		SleepThread(100);
	}
	return true;
}
void UnixWaitProcess(UINT pid)
{
	UnixWaitProcessEx(pid, INFINITE);
}

// Description of how to start
void UnixUsage(char *name)
{
	char *svc_name, *svc_title;
	char tmp[128];
	// Validate arguments
	if (name == NULL)
	{
		return;
	}

	Format(tmp, sizeof(tmp), SVC_NAME, name);
	svc_name = _SS(tmp);
	Format(tmp, sizeof(tmp), SVC_TITLE, name);
	svc_title = _SS(tmp);

	UniPrint(_UU("UNIX_SVC_HELP"), svc_title, svc_name, svc_name, svc_title, svc_name, svc_title);
}

// Main function of the UNIX service
UINT UnixService(int argc, char *argv[], char *name, SERVICE_FUNCTION *start, SERVICE_FUNCTION *stop)
{
	// Validate arguments
	if (name == NULL || start == NULL || stop == NULL)
	{
		return 0;
	}

	if (argc >= 2 && StrCmpi(argv[1], UNIX_SVC_ARG_EXEC_SVC) == 0)
	{
		UINT pid;
		// Start a child process
		// Restart if the child process didn't exit properly

RESTART_PROCESS:
		pid = fork();
		if ((int)pid != -1)
		{
			if (pid == 0)
			{
				// Run the main process
				UnixServiceMain(argc, argv, name, start, stop);
			}
			else
			{
				int status = 0, ret;

				// Wait for the termination of the child process
				ret = waitpid(pid, &status, 0);

				if (WIFEXITED(status) == 0)
				{
					// Aborted
					UnixSleep(100);
					goto RESTART_PROCESS;
				}
			}
		}
	}
	else
	{
		// Start normally
		UnixServiceMain(argc, argv, name, start, stop);
	}

	return 0;
}
void UnixServiceMain(int argc, char *argv[], char *name, SERVICE_FUNCTION *start, SERVICE_FUNCTION *stop)
{
	UINT mode = 0;
	// Start of the Mayaqua
	InitMayaqua(false, false, argc, argv);

	if (argc >= 2)
	{
		if (StrCmpi(argv[1], UNIX_SVC_ARG_START) == 0)
		{
			mode = UNIX_SVC_MODE_START;
		}
		if (StrCmpi(argv[1], UNIX_SVC_ARG_STOP) == 0)
		{
			mode = UNIX_SVC_MODE_STOP;
		}
		if (StrCmpi(argv[1], UNIX_SVC_ARG_EXEC_SVC) == 0)
		{
			mode = UNIX_SVC_MODE_EXEC_SVC;
		}
		if (StrCmpi(argv[1], UNIX_ARG_EXIT) == 0)
		{
			mode = UNIX_SVC_MODE_EXIT;
		}
	}

	switch (mode)
	{
	case UNIX_SVC_MODE_EXIT:
		break;

	case UNIX_SVC_MODE_START:
		UnixStartService(name);
		break;

	case UNIX_SVC_MODE_STOP:
		UnixStopService(name);
		break;

	case UNIX_SVC_MODE_EXEC_SVC:
		UnixExecService(name, start, stop);
		break;

	default:
		UnixUsage(name);
		break;
	}

	// End of the Mayaqua
	FreeMayaqua();

	return;
}

#endif	// UNIX
