Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/SoftEtherVPN/SoftEtherVPN_Stable.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'src/Mayaqua/Unix.c')
-rw-r--r--src/Mayaqua/Unix.c2741
1 files changed, 2741 insertions, 0 deletions
diff --git a/src/Mayaqua/Unix.c b/src/Mayaqua/Unix.c
new file mode 100644
index 00000000..8ee0b9c3
--- /dev/null
+++ b/src/Mayaqua/Unix.c
@@ -0,0 +1,2741 @@
+// SoftEther VPN Source Code
+// Mayaqua Kernel
+//
+// SoftEther VPN Server, Client and Bridge are free software under GPLv2.
+//
+// Copyright (c) 2012-2014 Daiyuu Nobori.
+// Copyright (c) 2012-2014 SoftEther VPN Project, University of Tsukuba, Japan.
+// Copyright (c) 2012-2014 SoftEther Corporation.
+//
+// All Rights Reserved.
+//
+// http://www.softether.org/
+//
+// Author: Daiyuu Nobori
+// Comments: Tetsuo Sugiyama, Ph.D.
+//
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// version 2 as published by the Free Software Foundation.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License version 2
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+// THE LICENSE AGREEMENT IS ATTACHED ON THE SOURCE-CODE PACKAGE
+// AS "LICENSE.TXT" FILE. READ THE TEXT FILE IN ADVANCE TO USE THE SOFTWARE.
+//
+//
+// THIS SOFTWARE IS DEVELOPED IN JAPAN, AND DISTRIBUTED FROM JAPAN,
+// UNDER JAPANESE LAWS. YOU MUST AGREE IN ADVANCE TO USE, COPY, MODIFY,
+// MERGE, PUBLISH, DISTRIBUTE, SUBLICENSE, AND/OR SELL COPIES OF THIS
+// SOFTWARE, THAT ANY JURIDICAL DISPUTES WHICH ARE CONCERNED TO THIS
+// SOFTWARE OR ITS CONTENTS, AGAINST US (SOFTETHER PROJECT, SOFTETHER
+// CORPORATION, DAIYUU NOBORI OR OTHER SUPPLIERS), OR ANY JURIDICAL
+// DISPUTES AGAINST US WHICH ARE CAUSED BY ANY KIND OF USING, COPYING,
+// MODIFYING, MERGING, PUBLISHING, DISTRIBUTING, SUBLICENSING, AND/OR
+// SELLING COPIES OF THIS SOFTWARE SHALL BE REGARDED AS BE CONSTRUED AND
+// CONTROLLED BY JAPANESE LAWS, AND YOU MUST FURTHER CONSENT TO
+// EXCLUSIVE JURISDICTION AND VENUE IN THE COURTS SITTING IN TOKYO,
+// JAPAN. YOU MUST WAIVE ALL DEFENSES OF LACK OF PERSONAL JURISDICTION
+// AND FORUM NON CONVENIENS. PROCESS MAY BE SERVED ON EITHER PARTY IN
+// THE MANNER AUTHORIZED BY APPLICABLE LAW OR COURT RULE.
+//
+// USE ONLY IN JAPAN. DO NOT USE IT IN OTHER COUNTRIES. IMPORTING THIS
+// SOFTWARE INTO OTHER COUNTRIES IS AT YOUR OWN RISK. SOME COUNTRIES
+// PROHIBIT ENCRYPTED COMMUNICATIONS. USING THIS SOFTWARE IN OTHER
+// COUNTRIES MIGHT BE RESTRICTED.
+//
+//
+// 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.
+
+
+// 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>
+
+// Struct statfs for MacOS X
+#ifdef UNIX_MACOS
+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 */
+};
+#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), "/sbin/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)
+ {
+ return NULL;
+ }
+ else
+ {
+ ret = ZeroMalloc(sizeof(UNIXLOCKFILE));
+ ret->fd = fd;
+ StrCpy(ret->FileName, sizeof(ret->FileName), name);
+ return (void *)ret;
+ }
+}
+
+// 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);
+ }
+}
+
+// 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 Liunx 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;
+ 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);
+
+ gmtime_r(&now, &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)t.tv_sec * 1000LL + (UINT64)t.tv_nsec / 1000000LL;
+
+ if (akirame == false && ret == 0)
+ {
+ ret = TickRealtimeManual();
+ akirame = true;
+ }
+
+ 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);
+}
+
+// Initialize the library for UNIX
+void UnixInit()
+{
+ UNIXIO *o;
+
+ 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, UNIX_MAX_MEMORY);
+#endif // RLIMIT_CORE
+
+#ifdef RLIMIT_DATA
+ UnixSetResourceLimit(RLIMIT_DATA, UNIX_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, UNIX_MAX_MEMORY);
+#endif // RLIMIT_STACK
+
+#ifdef RLIMIT_RSS
+ UnixSetResourceLimit(RLIMIT_RSS, UNIX_MAX_MEMORY);
+#endif // RLIMIT_RSS
+
+#ifdef RLIMIT_LOCKS
+ UnixSetResourceLimit(RLIMIT_LOCKS, UNIX_MAX_LOCKS);
+#endif // RLIMIT_LOCKS
+
+#ifdef RLIMIT_MEMLOCK
+ UnixSetResourceLimit(RLIMIT_MEMLOCK, UNIX_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, UINT value)
+{
+ struct rlimit t;
+ UINT hard_limit;
+
+ Zero(&t, sizeof(t));
+ getrlimit(id, &t);
+
+ hard_limit = t.rlim_max;
+
+ Zero(&t, sizeof(t));
+ t.rlim_cur = MIN(value, hard_limit);
+ t.rlim_max = hard_limit;
+ setrlimit(id, &t);
+
+ Zero(&t, sizeof(t));
+ t.rlim_cur = value;
+ t.rlim_max = value;
+ setrlimit(id, &t);
+}
+
+// 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();
+}
+
+// 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);
+ 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
+ {
+ UniPrint(_UU("UNIX_SVC_STOP_FAILED"), svc_title);
+ }
+ }
+
+ 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)
+ {
+#ifndef UNIX_BSD
+ pause();
+#else // UNIX_BSD
+ if (UnixReadCtlFile() != saved_ctl)
+ {
+ break;
+ }
+
+ SleepThread(1394);
+#endif // UNIX_BSD
+ }
+
+ // 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
+
+// Developed by SoftEther VPN Project at University of Tsukuba in Japan.
+// Department of Computer Science has dozens of overly-enthusiastic geeks.
+// Join us: http://www.tsukuba.ac.jp/english/admission/