text: // SoftEther VPN Source Code - Developer Edition Master Branch
  // Mayaqua Kernel
  
  
  // 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 <sys/utsname.h>
  #include <Mayaqua/Mayaqua.h>
  
  #ifdef	UNIX_MACOS
  #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)(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);
  	(void)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;
  
  	(void)poll(&p, 1, msec == INFINITE ? -1 : (int)msec);
  }
  
  // Get the free space of the disk
  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);
  }
  
  // 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;
  
  	(void)fcntl(o->fd, F_SETLK, &lock);
  	close(o->fd);
  
  	(void)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);
  	}
  
  	GetPidDir(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);
  	(void)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)
  {
  	struct utsname unix_info;
  
  	// Validate arguments
  	if (info == NULL)
  	{
  		return;
  	}
  
  	Zero(info, sizeof(OS_INFO));
  
  #ifdef	UNIX_SOLARIS
  	info->OsType = OSTYPE_SOLARIS;
  #elif	UNIX_CYGWIN
  	info->OsType = OSTYPE_CYGWIN;
  #elif	UNIX_MACOS
  	info->OsType = OSTYPE_MACOS_X;
  #elif	UNIX_BSD
  	info->OsType = OSTYPE_BSD;
  #elif	UNIX_LINUX
  	info->OsType = OSTYPE_LINUX;
  #else
  	info->OsType = OSTYPE_UNIX_UNKNOWN;
  #endif
  
  	info->OsSystemName = CopyStr(OsTypeToStr(info->OsType));
  	info->KernelName = CopyStr("UNIX");
  
  	if (uname(&unix_info) > -1)
  	{
  		info->OsProductName = CopyStr(unix_info.sysname);
  		info->OsVersion = CopyStr(unix_info.release);
  		info->KernelVersion = CopyStr(unix_info.version);
  	}
  	else
  	{
  		Debug("UnixGetOsInfo(): uname() failed with error: %s\n", strerror(errno));
  
  		info->OsProductName = CopyStr(OsTypeToStr(info->OsType));
  		info->OsVersion = CopyStr("Unknown");
  		info->KernelVersion = CopyStr("Unknown");
  	}
  #ifdef	UNIX_LINUX
  	{
  		BUF *buffer = ReadDump("/etc/os-release");
  		if (buffer == NULL)
  		{
  			buffer = ReadDump("/usr/lib/os-release");
  		}
  
  		if (buffer != NULL)
  		{
  			LIST *values = NewEntryList(buffer->Buf, "\n", "=");
  
  			FreeBuf(buffer);
  
  			if (EntryListHasKey(values, "NAME"))
  			{
  				char *str = EntryListStrValue(values, "NAME");
  				TrimQuotes(str);
  				Free(info->OsProductName);
  				info->OsProductName = CopyStr(str);
  			}
  
  			if (EntryListHasKey(values, "HOME_URL"))
  			{
  				char *str = EntryListStrValue(values, "HOME_URL");
  				TrimQuotes(str);
  				info->OsVendorName = CopyStr(str);
  			}
  
  			if (EntryListHasKey(values, "VERSION"))
  			{
  				char *str = EntryListStrValue(values, "VERSION");
  				TrimQuotes(str);
  				Free(info->OsVersion);
  				info->OsVersion = CopyStr(str);
  			}
  			else
  			{
  				// Debian testing/sid doesn't provide the version in /etc/os-release
  				buffer = ReadDump("/etc/debian_version");
  				if (buffer != NULL)
  				{
  					Free(info->OsVersion);
  					info->OsVersion = CfgReadNextLine(buffer);
  					FreeBuf(buffer);
  				}
  			}
  
  			FreeEntryList(values);
  		}
  	}
  #endif
  }
  
  // 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;
  	char **args;
  	UINT ret;
  
  	// Validate arguments
  	if (filename == NULL)
  	{
  		return false;
  	}
  	if (arg == NULL)
  	{
  		arg = "";
  	}
  
  	Print("", filename, arg);
  	t = ParseToken(arg, " ");
  	if (t == NULL)
  	{
  		return false;
  	}
  	else
  	{
  		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];
  		}
  	}
  	
  	// Create a child process
  	ret = fork();
  	if (ret == -1)
  	{
  		// Child process creation failure
  		return false;
  	}
  
  	if (ret == 0)
  	{
  		// Child process
  		if (hide)
  		{
  			// Close the standard I/O
  			UnixCloseIO();
  		}
  		execvp(filename, args);
  		AbortExit();
  	}
  	else
  	{
  		// Parent process
  		pid_t pid = (pid_t)ret;
  		Free(args);
  		FreeToken(t);
  
  		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;
  	}
  }
  
  // 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);
  		(void)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;
  }
  
  // Get UNIXIO object for stdout
  void* GetUnixio4Stdout()
  {
  	static UNIXIO unixio =
  	{
  		.fd = -1,
  		.write_mode = true
  	};
  
  	if (g_foreground)
  	{
  		unixio.fd = STDOUT_FILENO;
  		return &unixio;
  	}
  	return NULL;
  }
  
  // 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;
  		(void)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));
  	if (mutex == NULL)
  	{
  		return NULL;
  	}
  
  	// 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)((UINT)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;
  
  	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);
  #elif	CLOCK_MONOTONIC
  	clock_gettime(CLOCK_MONOTONIC, &t);
  #else
  	clock_gettime(CLOCK_REALTIME, &t);
  #endif
  
  	ret = ((UINT64)((UINT)t.tv_sec)) * 1000LL + (UINT64)t.tv_nsec / 1000000LL;
  
  	if (ret == 0)
  	{
  		ret = TickRealtimeManual();
  	}
  
  	return ret;
  #else
  	return TickRealtimeManual();
  #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;
  	}
  
  	GetPidDir(dir, sizeof(dir));
  
  	GetExeName(exe_name, sizeof(exe_name));
  	StrCat(exe_name, sizeof(exe_name), ":pid_hash");
  	StrUpper(exe_name);
  
  	Md5(hash, exe_name, StrLen(exe_name));
  	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;
  	}
  
  	GetPidDir(dir, sizeof(dir));
  
  	GetExeName(exe_name, sizeof(exe_name));
  	StrCat(exe_name, sizeof(exe_name), ":pid_hash");
  	StrUpper(exe_name);
  
  	Md5(hash, exe_name, StrLen(exe_name));
  	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();
  }
  
  // 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"));
  		}
  
  		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
  	{
  		// 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;
  }
  
  // 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 if (argc >= 3 && StrCmpi(argv[1], UNIX_SVC_ARG_START) == 0 && StrCmpi(argv[2], UNIX_SVC_ARG_FOREGROUND) == 0)
  	{
  #ifdef DEBUG
  		// If set memcheck = true, the program will be vitally slow since it will log all malloc() / realloc() / free() calls to find the cause of memory leak.
  		// For normal debug we set memcheck = false.
  		// Please set memcheck = true if you want to test the cause of memory leaks.
  		InitMayaqua(false, true, argc, argv);
  #else
  		InitMayaqua(false, false, argc, argv);
  #endif
  		UnixExecService(name, start, stop);
  		FreeMayaqua();
  	}
  	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
  #ifdef DEBUG
  	// If set memcheck = true, the program will be vitally slow since it will log all malloc() / realloc() / free() calls to find the cause of memory leak.
  	// For normal debug we set memcheck = false.
  	// Please set memcheck = true if you want to test the cause of memory leaks.
  	InitMayaqua(false, true, argc, argv);
  #else
  	InitMayaqua(false, false, argc, argv);
  #endif
  
  	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
  