1
0
mirror of https://github.com/SoftEtherVPN/SoftEtherVPN.git synced 2025-04-03 18:00:08 +03:00
SoftEtherVPN/src/Mayaqua/Unix.c
Daiyuu Nobori 2dec52b875 Heap area protection of memory has been enhanced.
When memory is released and reallocated, a random security value called a canary is written to the before/after area of memory, and if the value has been modified, the process is terminated (restarted) for safety, assuming it is a buffer overflow of the memory area. This feature may effectively prevent confidentiality or integrity violations in the event that some heap area overflow vulnerability is discovered in this system in the future.
2023-10-07 04:42:34 +02:00

2854 lines
52 KiB
C
Executable File

// SoftEther VPN Source Code - Developer Edition Master Branch
// Mayaqua Kernel
// Unix.c
// UNIX dependent code
#ifdef OS_UNIX
#include "Unix.h"
#include "Cfg.h"
#include "FileIO.h"
#include "GlobalConst.h"
#include "Internat.h"
#include "Kernel.h"
#include "Mayaqua.h"
#include "Memory.h"
#include "Network.h"
#include "Object.h"
#include "Str.h"
#include "Table.h"
#include "Tick64.h"
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include <fcntl.h>
#include <poll.h>
#include <pthread.h>
#include <signal.h>
#include <sys/mount.h>
#include <sys/param.h>
#include <sys/resource.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/utsname.h>
#include <sys/wait.h>
#ifdef UNIX_LINUX
#include <sys/statfs.h>
#endif
#ifdef UNIX_SOLARIS
#define USE_STATVFS
#include <sys/statvfs.h>
#endif
#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)
{
(void)sig;
(void)info;
(void)ucontext;
}
// 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 Linux/FreeBSD is running in a VM
// Not implemented yet on other OS
bool UnixIsInVmMain()
{
TOKEN_LIST *t = NULL;
bool ret = false;
#if defined(UNIX_LINUX)
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";
#elif defined(__FreeBSD__)
char *vm_str_list = "generic,xen,hv,vmware,kvm,bhyve";
#endif
#if defined(UNIX_LINUX)
t = UnixExec("/bin/dmesg");
#elif defined(__FreeBSD__)
t = UnixExec("/sbin/sysctl -n kern.vm_guest");
#endif
#if defined(UNIX_LINUX) || defined(__FreeBSD__)
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 // defined(UNIX_LINUX) || defined(__FreeBSD)
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));
if (lock == NULL)
{
return NULL;
}
// Create a mutex
mutex = UnixMemoryAlloc(sizeof(pthread_mutex_t));
if (mutex == NULL)
{
UnixMemoryFree(lock);
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);
}
UINT64 UnixGetHighresTickNano64(bool raw)
{
#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 UnixGetTick64() * 1000000ULL;
}
Zero(&t, sizeof(t));
if (raw == false)
{
// 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
}
else
{
// 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_RAW
clock_gettime(CLOCK_MONOTONIC_RAW, &t);
#else // CLOCK_MONOTONIC_RAW
#ifdef CLOCK_MONOTONIC
clock_gettime(CLOCK_MONOTONIC, &t);
#else // CLOCK_MONOTONIC
clock_gettime(CLOCK_REALTIME, &t);
#endif // CLOCK_MONOTONIC
#endif // CLOCK_MONOTONIC_RAW
#endif // CLOCK_HIGHRES
}
ret = ((UINT64)((UINT)t.tv_sec)) * 1000000000LL + (UINT64)t.tv_nsec;
if (akirame == false && ret == 0)
{
ret = UnixGetTick64() * 1000000ULL;
akirame = true;
}
return ret;
#else
return UnixGetTick64() * 1000000ULL;
#endif
}
// 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