1
0
mirror of https://github.com/SoftEtherVPN/SoftEtherVPN.git synced 2024-11-26 19:39:53 +03:00
SoftEtherVPN/src/Mayaqua/Unix.c
2019-12-04 23:59:07 +09:00

2740 lines
49 KiB
C
Executable File

// SoftEther VPN Source Code - Developer Edition Master Branch
// Mayaqua Kernel
// Unix.c
// UNIX dependent code
#include <GlobalConst.h>
#ifdef UNIX
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <wchar.h>
#include <stdarg.h>
#include <time.h>
#include <errno.h>
#include <sys/utsname.h>
#include <Mayaqua/Mayaqua.h>
#ifdef UNIX_MACOS
#ifdef NO_VLAN
// Struct statfs for MacOS X
typedef struct fsid { int32_t val[2]; } fsid_t;
struct statfs {
short f_otype; /* TEMPORARY SHADOW COPY OF f_type */
short f_oflags; /* TEMPORARY SHADOW COPY OF f_flags */
long f_bsize; /* fundamental file system block size */
long f_iosize; /* optimal transfer block size */
long f_blocks; /* total data blocks in file system */
long f_bfree; /* free blocks in fs */
long f_bavail; /* free blocks avail to non-superuser */
long f_files; /* total file nodes in file system */
long f_ffree; /* free file nodes in fs */
fsid_t f_fsid; /* file system id */
uid_t f_owner; /* user that mounted the filesystem */
short f_reserved1; /* spare for later */
short f_type; /* type of filesystem */
long f_flags; /* copy of mount exported flags */
long f_reserved2[2]; /* reserved for future use */
char f_fstypename[15]; /* fs type name */
char f_mntonname[90]; /* directory on which mounted */
char f_mntfromname[90];/* mounted filesystem */
};
#else // NO_VLAN
#include <sys/mount.h>
#endif // NO_VLAN
#endif // UNIX_MACOS
// Scandir() function for Solaris
#ifdef UNIX_SOLARIS
#define scandir local_scandir
#define alphasort local_alphasort
int local_scandir(const char *dir, struct dirent ***namelist,
int (*select)(const struct dirent *),
int (*compar)(const struct dirent **, const struct dirent **))
{
DIR *d;
struct dirent *entry;
register int i=0;
size_t entrysize;
if ((d=opendir(dir)) == NULL)
return(-1);
*namelist=NULL;
while ((entry=readdir(d)) != NULL)
{
if (select == NULL || (*select)(entry))
{
*namelist=(struct dirent **)realloc((void *)(*namelist),
(size_t)((i+1)*sizeof(struct dirent *)));
if (*namelist == NULL) return(-1);
entrysize=sizeof(struct dirent)-sizeof(entry->d_name)+strlen(entry->d_name)+1;
(*namelist)[i]=(struct dirent *)malloc(entrysize);
if ((*namelist)[i] == NULL) return(-1);
memcpy((*namelist)[i], entry, entrysize);
i++;
}
}
if (closedir(d)) return(-1);
if (i == 0) return(-1);
if (compar != NULL)
qsort((void *)(*namelist), (size_t)i, sizeof(struct dirent *), compar);
return(i);
}
int local_alphasort(const struct dirent **a, const struct dirent **b)
{
return(strcmp((*a)->d_name, (*b)->d_name));
}
#endif // UNIX_SOLARIS
// Thread data for UNIX
typedef struct UNIXTHREAD
{
pthread_t thread;
bool finished;
} UNIXTHREAD;
// Thread startup information for UNIX
typedef struct UNIXTHREADSTARTUPINFO
{
THREAD_PROC *thread_proc;
void *param;
THREAD *thread;
} UNIXTHREADSTARTUPINFO;
// Thread function prototype for UNIX
void *UnixDefaultThreadProc(void *param);
// Current process ID
static pid_t current_process_id = 0;
// File I/O data for UNIX
typedef struct UNIXIO
{
int fd;
bool write_mode;
} UNIXIO;
// Lock file data for UNIX
typedef struct UNIXLOCKFILE
{
char FileName[MAX_SIZE];
int fd;
} UNIXLOCKFILE;
// Event data for UNIX
typedef struct UNIXEVENT
{
pthread_mutex_t mutex;
pthread_cond_t cond;
bool signal;
} UNIXEVENT;
static pthread_mutex_t get_time_lock;
static pthread_mutex_t malloc_lock;
static bool high_process = false;
static bool unix_svc_terminate = false;
static int solaris_sleep_p1 = -1, solaris_sleep_p2 = -1;
// Create a dispatch table
OS_DISPATCH_TABLE *UnixGetDispatchTable()
{
static OS_DISPATCH_TABLE t =
{
UnixInit,
UnixFree,
UnixMemoryAlloc,
UnixMemoryReAlloc,
UnixMemoryFree,
UnixGetTick,
UnixGetSystemTime,
UnixInc32,
UnixDec32,
UnixSleep,
UnixNewLock,
UnixLock,
UnixUnlock,
UnixDeleteLock,
UnixInitEvent,
UnixSetEvent,
UnixResetEvent,
UnixWaitEvent,
UnixFreeEvent,
UnixWaitThread,
UnixFreeThread,
UnixInitThread,
UnixThreadId,
UnixFileOpen,
UnixFileOpenW,
UnixFileCreate,
UnixFileCreateW,
UnixFileWrite,
UnixFileRead,
UnixFileClose,
UnixFileFlush,
UnixFileSize,
UnixFileSeek,
UnixFileDelete,
UnixFileDeleteW,
UnixMakeDir,
UnixMakeDirW,
UnixDeleteDir,
UnixDeleteDirW,
UnixGetCallStack,
UnixGetCallStackSymbolInfo,
UnixFileRename,
UnixFileRenameW,
UnixRun,
UnixRunW,
UnixIsSupportedOs,
UnixGetOsInfo,
UnixAlert,
UnixAlertW,
UnixGetProductId,
UnixSetHighPriority,
UnixRestorePriority,
UnixNewSingleInstance,
UnixFreeSingleInstance,
UnixGetMemInfo,
UnixYield,
};
return &t;
}
static void *signal_received_for_ignore(int sig, siginfo_t *info, void *ucontext)
{
return NULL;
}
// Ignore the signal flew to the thread
void UnixIgnoreSignalForThread(int sig)
{
struct sigaction sa;
Zero(&sa, sizeof(sa));
sa.sa_handler = NULL;
sa.sa_sigaction = signal_received_for_ignore;
sa.sa_flags = SA_SIGINFO;
sigemptyset(&sa.sa_mask);
sigaction(SIGUSR1, &sa, NULL);
}
// Disable the off-loading function of the specific Ethernet device
void UnixDisableInterfaceOffload(char *name)
{
#ifdef UNIX_LINUX
char tmp[MAX_SIZE];
TOKEN_LIST *t;
char *names = "rx tx sg tso ufo gso gro lro rxvlan txvlan ntuple rxhash";
// Validate arguments
if (name == NULL)
{
return;
}
t = ParseToken(names, " ");
if (t != NULL)
{
UINT i;
for (i = 0;i < t->NumTokens;i++)
{
char *a = t->Token[i];
Format(tmp, sizeof(tmp), "ethtool -K %s %s off 2>/dev/null", name, a);
FreeToken(UnixExec(tmp));
}
}
FreeToken(t);
#endif // UNIX_LINUX
}
// Validate whether the UNIX is running in a VM
bool UnixIsInVmMain()
{
TOKEN_LIST *t = NULL;
bool ret = false;
char *vm_str_list = "Hypervisor detected,VMware Virtual Platform,VMware Virtual USB,qemu,xen,paravirtualized,virtual hd,virtualhd,virtual pc,virtualpc,kvm,oracle vm,oraclevm,parallels,xvm,bochs";
#ifdef UNIX_LINUX
t = UnixExec("/bin/dmesg");
if (t != NULL)
{
BUF *b = NewBuf();
UINT i;
for (i = 0;i < t->NumTokens;i++)
{
char *line = t->Token[i];
AddBufStr(b, line);
AddBufStr(b, " ");
}
WriteBufInt(b, 0);
// printf("%s\n", b->Buf);
ret = InStrList(b->Buf, vm_str_list, ",", false);
FreeBuf(b);
FreeToken(t);
}
#endif // UNIX_LINUX
return ret;
}
bool UnixIsInVm()
{
static bool is_in_vm_flag = false;
static bool is_in_vm_ret = false;
if (is_in_vm_flag == false)
{
is_in_vm_ret = UnixIsInVmMain();
is_in_vm_flag = true;
}
return is_in_vm_ret;
}
// Run quietly in the UNIX
void UnixExecSilent(char *cmd)
{
char tmp[MAX_SIZE];
// Validate arguments
if (cmd == NULL)
{
return;
}
Format(tmp, sizeof(tmp), "%s 2>/dev/null", cmd);
FreeToken(UnixExec(tmp));
}
// Enable / disable the ESP processing in the kernel
void UnixSetEnableKernelEspProcessing(bool b)
{
if (GetOsInfo()->OsType == OSTYPE_MACOS_X)
{
// Mac OS X
if (b)
{
UnixExecSilent("/usr/sbin/sysctl -w net.inet.ipsec.esp_port=4500");
}
else
{
UnixExecSilent("/usr/sbin/sysctl -w net.inet.ipsec.esp_port=4501");
}
}
}
// Run a command and return its result
TOKEN_LIST *UnixExec(char *cmd)
{
FILE *fp;
char tmp[MAX_SIZE];
char *ptr;
LIST *o;
UINT i;
TOKEN_LIST *ret;
// Validate arguments
if (cmd == NULL)
{
return NULL;
}
fp = popen(cmd, "r");
if (fp == NULL)
{
return NULL;
}
o = NewList(NULL);
while (true)
{
fgets(tmp, sizeof(tmp), fp);
if (feof(fp))
{
break;
}
ptr = strchr(tmp, '\n');
if (ptr != NULL)
{
*ptr = 0;
}
ptr = strchr(tmp, '\r');
if (ptr != NULL)
{
*ptr = 0;
}
Add(o, CopyStr(tmp));
}
pclose(fp);
ret = ListToTokenList(o);
FreeStrList(o);
return ret;
}
// Initialize the Sleep for Solaris
void UnixInitSolarisSleep()
{
char tmp[MAX_SIZE];
UnixNewPipe(&solaris_sleep_p1, &solaris_sleep_p2);
(void)read(solaris_sleep_p1, tmp, sizeof(tmp));
}
// Release the Sleep for Solaris
void UnixFreeSolarisSleep()
{
UnixDeletePipe(solaris_sleep_p1, solaris_sleep_p2);
solaris_sleep_p1 = -1;
solaris_sleep_p2 = -1;
}
// Sleep for Solaris
void UnixSolarisSleep(UINT msec)
{
struct pollfd p;
memset(&p, 0, sizeof(p));
p.fd = solaris_sleep_p1;
p.events = POLLIN;
(void)poll(&p, 1, msec == INFINITE ? -1 : (int)msec);
}
// Get the free space of the disk
bool UnixGetDiskFree(char *path, UINT64 *free_size, UINT64 *used_size, UINT64 *total_size)
{
char tmp[MAX_PATH];
bool ret = false;
// Validate arguments
if (path == NULL)
{
return false;
}
NormalizePath(tmp, sizeof(tmp), path);
while ((ret = UnixGetDiskFreeMain(tmp, free_size, used_size, total_size)) == false)
{
if (StrCmpi(tmp, "/") == 0)
{
break;
}
GetDirNameFromFilePath(tmp, sizeof(tmp), tmp);
}
return ret;
}
bool UnixGetDiskFreeMain(char *path, UINT64 *free_size, UINT64 *used_size, UINT64 *total_size)
{
#ifndef USE_STATVFS
struct statfs st;
char tmp[MAX_PATH];
UINT64 v1 = 0, v2 = 0;
bool ret = false;
// Validate arguments
if (path == NULL)
{
return false;
}
NormalizePath(tmp, sizeof(tmp), path);
Zero(&st, sizeof(st));
if (statfs(tmp, &st) == 0)
{
v1 = (UINT64)st.f_bsize * (UINT64)st.f_bavail;
v2 = (UINT64)st.f_bsize * (UINT64)st.f_blocks;
ret = true;
}
if (free_size != NULL)
{
*free_size = v1;
}
if (total_size != NULL)
{
*total_size = v2;
}
if (used_size != NULL)
{
*used_size = v2 - v1;
}
return ret;
#else // USE_STATVFS
struct statvfs st;
char tmp[MAX_PATH];
UINT64 v1 = 0, v2 = 0;
bool ret = false;
// Validate arguments
if (path == NULL)
{
return false;
}
NormalizePath(tmp, sizeof(tmp), path);
Zero(&st, sizeof(st));
if (statvfs(tmp, &st) == 0)
{
v1 = (UINT64)st.f_bsize * (UINT64)st.f_bavail;
v2 = (UINT64)st.f_bsize * (UINT64)st.f_blocks;
ret = true;
}
if (free_size != NULL)
{
*free_size = v1;
}
if (total_size != NULL)
{
*total_size = v2;
}
if (used_size != NULL)
{
*used_size = v2 - v1;
}
return ret;
#endif // USE_STATVFS
}
// Directory enumeration
DIRLIST *UnixEnumDirEx(char *dirname, COMPARE *compare)
{
char tmp[MAX_PATH];
DIRLIST *d;
int n;
struct dirent **e;
LIST *o;
// Validate arguments
if (dirname == NULL)
{
return NULL;
}
o = NewListFast(compare);
NormalizePath(tmp, sizeof(tmp), dirname);
if (StrLen(tmp) >= 1 && tmp[StrLen(tmp) - 1] != '/')
{
StrCat(tmp, sizeof(tmp), "/");
}
e = NULL;
n = scandir(tmp, &e, 0, alphasort);
if (StrLen(tmp) >= 1 && tmp[StrLen(tmp) - 1] == '/')
{
tmp[StrLen(tmp) - 1] = 0;
}
if (n >= 0 && e != NULL)
{
UINT i;
for (i = 0;i < (UINT)n;i++)
{
char *filename = e[i]->d_name;
if (filename != NULL)
{
if (StrCmpi(filename, "..") != 0 && StrCmpi(filename, ".") != 0)
{
char fullpath[MAX_PATH];
struct stat st;
Format(fullpath, sizeof(fullpath), "%s/%s", tmp, filename);
Zero(&st, sizeof(st));
if (stat(fullpath, &st) == 0)
{
DIRENT *f = ZeroMalloc(sizeof(DIRENT));
SYSTEMTIME t;
f->Folder = S_ISDIR(st.st_mode) ? true : false;
f->FileName = CopyStr(filename);
f->FileNameW = CopyUtfToUni(f->FileName);
Zero(&t, sizeof(t));
TimeToSystem(&t, st.st_ctime);
f->CreateDate = SystemToUINT64(&t);
Zero(&t, sizeof(t));
TimeToSystem(&t, st.st_mtime);
f->UpdateDate = SystemToUINT64(&t);
if (f->Folder == false)
{
f->FileSize = st.st_size;
}
Add(o, f);
}
}
}
free(e[i]);
}
free(e);
}
Sort(o);
d = ZeroMalloc(sizeof(DIRLIST));
d->NumFiles = LIST_NUM(o);
d->File = ToArray(o);
ReleaseList(o);
return d;
}
DIRLIST *UnixEnumDirExW(wchar_t *dirname, COMPARE *compare)
{
char *dirname_a = CopyUniToUtf(dirname);
DIRLIST *ret;
ret = UnixEnumDirEx(dirname_a, compare);
Free(dirname_a);
return ret;
}
// Check the execute permissions of the specified file
bool UnixCheckExecAccess(char *name)
{
// Validate arguments
if (name == NULL)
{
return false;
}
if (access(name, X_OK) == 0)
{
return true;
}
return false;
}
bool UnixCheckExecAccessW(wchar_t *name)
{
char *name_a;
bool ret;
// Validate arguments
if (name == NULL)
{
return false;
}
name_a = CopyUniToUtf(name);
ret = UnixCheckExecAccess(name_a);
Free(name_a);
return ret;
}
// Raise the priority of the thread to highest
void UnixSetThreadPriorityRealtime()
{
struct sched_param p;
Zero(&p, sizeof(p));
p.sched_priority = 255;
pthread_setschedparam(pthread_self(), SCHED_RR, &p);
}
// Get the current directory
void UnixGetCurrentDir(char *dir, UINT size)
{
// Validate arguments
if (dir == NULL)
{
return;
}
getcwd(dir, size);
}
void UnixGetCurrentDirW(wchar_t *dir, UINT size)
{
char dir_a[MAX_PATH];
UnixGetCurrentDir(dir_a, sizeof(dir_a));
UtfToUni(dir, size, dir_a);
}
// Yield
void UnixYield()
{
#ifdef UNIX_SOLARIS
UnixSolarisSleep(1);
#else
usleep(1000);
#endif
}
// Get the memory information
void UnixGetMemInfo(MEMINFO *info)
{
// Validate arguments
if (info == NULL)
{
return;
}
// I don't know!!
Zero(info, sizeof(MEMINFO));
}
// Release of the single instance
void UnixFreeSingleInstance(void *data)
{
UNIXLOCKFILE *o;
struct flock lock;
// Validate arguments
if (data == NULL)
{
return;
}
o = (UNIXLOCKFILE *)data;
Zero(&lock, sizeof(lock));
lock.l_type = F_UNLCK;
lock.l_whence = SEEK_SET;
(void)fcntl(o->fd, F_SETLK, &lock);
close(o->fd);
(void)remove(o->FileName);
Free(data);
}
// Creating a single instance
void *UnixNewSingleInstance(char *instance_name)
{
UNIXLOCKFILE *ret;
char tmp[MAX_SIZE];
char name[MAX_SIZE];
char dir[MAX_PATH];
int fd;
struct flock lock;
int mode = S_IRUSR | S_IWUSR;
// Validate arguments
if (instance_name == NULL)
{
GetExeName(tmp, sizeof(tmp));
HashInstanceName(tmp, sizeof(tmp), tmp);
}
else
{
StrCpy(tmp, sizeof(tmp), instance_name);
}
GetPidDir(dir, sizeof(dir));
// File name generation
Format(name, sizeof(name), "%s/.%s", dir, tmp);
fd = open(name, O_WRONLY);
if (fd == -1)
{
fd = creat(name, mode);
}
if (fd == -1)
{
Format(tmp, sizeof(tmp), "Unable to create %s.", name);
Alert(tmp, NULL);
exit(0);
return NULL;
}
fchmod(fd, mode);
(void)chmod(name, mode);
Zero(&lock, sizeof(lock));
lock.l_type = F_WRLCK;
lock.l_whence = SEEK_SET;
if (fcntl(fd, F_SETLK, &lock) == -1)
{
close(fd);
(void)remove(name);
return NULL;
}
else
{
ret = ZeroMalloc(sizeof(UNIXLOCKFILE));
ret->fd = fd;
StrCpy(ret->FileName, sizeof(ret->FileName), name);
return (void *)ret;
}
}
// Set the high oom score
void UnixSetHighOomScore()
{
IO *o;
char tmp[256];
sprintf(tmp, "/proc/%u/oom_score_adj", getpid());
o = UnixFileCreate(tmp);
if (o != NULL)
{
char tmp[128];
sprintf(tmp, "%u\n", 800);
UnixFileWrite(o, tmp, strlen(tmp));
UnixFileClose(o, false);
}
}
// Raise the priority of the process
void UnixSetHighPriority()
{
if (high_process == false)
{
UINT pid = getpid();
UINT pgid = getpgid(pid);
high_process = true;
nice(-20);
setpriority(PRIO_PROCESS, pid, -20);
setpriority(PRIO_PGRP, pgid, -20);
}
}
// Restore the priority of the process
void UnixRestorePriority()
{
if (high_process != false)
{
high_process = false;
nice(20);
}
}
UINT UnixGetNumberOfCpuInner()
{
BUF *b;
UINT ret = 0;
b = ReadDump("/proc/cpuinfo");
if (b != NULL)
{
while (true)
{
char *line = CfgReadNextLine(b);
if (line == NULL)
{
break;
}
if (IsEmptyStr(line) == false)
{
TOKEN_LIST *t = ParseToken(line, ":");
if (t != NULL)
{
if (t->NumTokens >= 2)
{
char *key = t->Token[0];
char *value = t->Token[1];
Trim(key);
Trim(value);
if (StrCmpi(key, "processor") == 0)
{
if (IsNum(value))
{
UINT i = ToInt(value) + 1;
if (i >= 1 && i <= 128)
{
ret = MAX(ret, i);
}
}
}
}
FreeToken(t);
}
}
Free(line);
}
FreeBuf(b);
}
return ret;
}
// Get the product ID
char *UnixGetProductId()
{
return CopyStr("--");
}
// Display an alert
void UnixAlertW(wchar_t *msg, wchar_t *caption)
{
char *msg8 = CopyUniToUtf(msg);
char *caption8 = CopyUniToUtf(caption);
UnixAlert(msg8, caption8);
Free(msg8);
Free(caption8);
}
void UnixAlert(char *msg, char *caption)
{
char *tag =
"-- Alert: %s --\n%s\n";
// Validate arguments
if (msg == NULL)
{
msg = "Alert";
}
if (caption == NULL)
{
caption = CEDAR_PRODUCT_STR " VPN Kernel";
}
printf(tag, caption, msg);
}
// Get the information of the current OS
void UnixGetOsInfo(OS_INFO *info)
{
struct utsname unix_info;
// Validate arguments
if (info == NULL)
{
return;
}
Zero(info, sizeof(OS_INFO));
#ifdef UNIX_SOLARIS
info->OsType = OSTYPE_SOLARIS;
#elif UNIX_CYGWIN
info->OsType = OSTYPE_CYGWIN;
#elif UNIX_MACOS
info->OsType = OSTYPE_MACOS_X;
#elif UNIX_BSD
info->OsType = OSTYPE_BSD;
#elif UNIX_LINUX
info->OsType = OSTYPE_LINUX;
#else
info->OsType = OSTYPE_UNIX_UNKNOWN;
#endif
info->OsSystemName = CopyStr(OsTypeToStr(info->OsType));
info->KernelName = CopyStr("UNIX");
if (uname(&unix_info) > -1)
{
info->OsProductName = CopyStr(unix_info.sysname);
info->OsVersion = CopyStr(unix_info.release);
info->KernelVersion = CopyStr(unix_info.version);
}
else
{
Debug("UnixGetOsInfo(): uname() failed with error: %s\n", strerror(errno));
info->OsProductName = CopyStr(OsTypeToStr(info->OsType));
info->OsVersion = CopyStr("Unknown");
info->KernelVersion = CopyStr("Unknown");
}
#ifdef UNIX_LINUX
{
BUF *buffer = ReadDump("/etc/os-release");
if (buffer == NULL)
{
buffer = ReadDump("/usr/lib/os-release");
}
if (buffer != NULL)
{
LIST *values = NewEntryList(buffer->Buf, "\n", "=");
FreeBuf(buffer);
if (EntryListHasKey(values, "NAME"))
{
char *str = EntryListStrValue(values, "NAME");
TrimQuotes(str);
Free(info->OsProductName);
info->OsProductName = CopyStr(str);
}
if (EntryListHasKey(values, "HOME_URL"))
{
char *str = EntryListStrValue(values, "HOME_URL");
TrimQuotes(str);
info->OsVendorName = CopyStr(str);
}
if (EntryListHasKey(values, "VERSION"))
{
char *str = EntryListStrValue(values, "VERSION");
TrimQuotes(str);
Free(info->OsVersion);
info->OsVersion = CopyStr(str);
}
else
{
// Debian testing/sid doesn't provide the version in /etc/os-release
buffer = ReadDump("/etc/debian_version");
if (buffer != NULL)
{
Free(info->OsVersion);
info->OsVersion = CfgReadNextLine(buffer);
FreeBuf(buffer);
}
}
FreeEntryList(values);
}
}
#endif
}
// Examine whether the current OS is supported by the PacketiX VPN Kernel
bool UnixIsSupportedOs()
{
// Support all UNIX OS which can run PacketiX VPN
return true;
}
// Run a specified command
bool UnixRunW(wchar_t *filename, wchar_t *arg, bool hide, bool wait)
{
char *filename8 = CopyUniToUtf(filename);
char *arg8 = CopyUniToUtf(arg);
bool ret = UnixRun(filename8, arg8, hide, wait);
Free(filename8);
Free(arg8);
return ret;
}
bool UnixRun(char *filename, char *arg, bool hide, bool wait)
{
TOKEN_LIST *t;
char **args;
UINT ret;
// Validate arguments
if (filename == NULL)
{
return false;
}
if (arg == NULL)
{
arg = "";
}
Print("", filename, arg);
t = ParseToken(arg, " ");
if (t == NULL)
{
return false;
}
else
{
UINT num_args;
UINT i;
num_args = t->NumTokens + 2;
args = ZeroMalloc(sizeof(char *) * num_args);
args[0] = filename;
for (i = 1;i < num_args - 1;i++)
{
args[i] = t->Token[i - 1];
}
}
// Create a child process
ret = fork();
if (ret == -1)
{
// Child process creation failure
return false;
}
if (ret == 0)
{
// Child process
if (hide)
{
// Close the standard I/O
UnixCloseIO();
}
execvp(filename, args);
AbortExit();
}
else
{
// Parent process
pid_t pid = (pid_t)ret;
Free(args);
FreeToken(t);
if (wait)
{
int status = 0;
// Wait for the termination of the child process
if (waitpid(pid, &status, 0) == -1)
{
return false;
}
if (WEXITSTATUS(status) == 0)
{
return true;
}
else
{
return false;
}
}
return true;
}
}
// Close the standard I/O
void UnixCloseIO()
{
static bool close_io_first = false;
// Execute only once
if (close_io_first)
{
return;
}
else
{
close(0);
close(1);
close(2);
(void)open("/dev/null", O_RDWR);
dup2(0, 1);
dup2(0, 2);
close_io_first = false;
}
}
// Change the file name
bool UnixFileRenameW(wchar_t *old_name, wchar_t *new_name)
{
char *old_name8 = CopyUniToUtf(old_name);
char *new_name8 = CopyUniToUtf(new_name);
bool ret = UnixFileRename(old_name8, new_name8);
Free(old_name8);
Free(new_name8);
return ret;
}
bool UnixFileRename(char *old_name, char *new_name)
{
// Validate arguments
if (old_name == NULL || new_name == NULL)
{
return false;
}
if (rename(old_name, new_name) != 0)
{
return false;
}
return true;
}
// Get the call stack
CALLSTACK_DATA *UnixGetCallStack()
{
// This is not supported on non-Win32
return NULL;
}
// Get the symbol information from the call stack
bool UnixGetCallStackSymbolInfo(CALLSTACK_DATA *s)
{
// This is not supported on non-Win32
return false;
}
// Delete the directory
bool UnixDeleteDirW(wchar_t *name)
{
char *name8 = CopyUniToUtf(name);
bool ret = UnixDeleteDir(name8);
Free(name8);
return ret;
}
bool UnixDeleteDir(char *name)
{
// Validate arguments
if (name == NULL)
{
return false;
}
if (rmdir(name) != 0)
{
return false;
}
return true;
}
// Create a directory
bool UnixMakeDirW(wchar_t *name)
{
char *name8 = CopyUniToUtf(name);
bool ret = UnixMakeDir(name8);
Free(name8);
return ret;
}
bool UnixMakeDir(char *name)
{
// Validate arguments
if (name == NULL)
{
return false;
}
if (mkdir(name, 0700) != 0)
{
return false;
}
return true;
}
// Delete the file
bool UnixFileDeleteW(wchar_t *name)
{
bool ret;
char *name8 = CopyUniToUtf(name);
ret = UnixFileDelete(name8);
Free(name8);
return ret;
}
bool UnixFileDelete(char *name)
{
// Validate arguments
if (name == NULL)
{
return false;
}
if (remove(name) != 0)
{
return false;
}
return true;
}
// Seek the file
bool UnixFileSeek(void *pData, UINT mode, int offset)
{
UNIXIO *p;
UINT ret;
// Validate arguments
if (pData == NULL)
{
return 0;
}
if (mode != FILE_BEGIN && mode != FILE_END && mode != FILE_CURRENT)
{
return false;
}
p = (UNIXIO *)pData;
ret = lseek(p->fd, offset, mode);
if (ret == -1)
{
return false;
}
return true;
}
// Get the file size
UINT64 UnixFileSize(void *pData)
{
struct stat st;
UNIXIO *p;
int r;
// Validate arguments
if (pData == NULL)
{
return 0;
}
p = (UNIXIO *)pData;
Zero(&st, sizeof(st));
r = fstat(p->fd, &st);
if (r != 0)
{
return 0;
}
return (UINT64)st.st_size;
}
// Write to the file
bool UnixFileWrite(void *pData, void *buf, UINT size)
{
UNIXIO *p;
UINT ret;
// Validate arguments
if (pData == NULL || buf == NULL || size == 0)
{
return false;
}
p = (UNIXIO *)pData;
ret = write(p->fd, buf, size);
if (ret != size)
{
return false;
}
return true;
}
// Read from the file
bool UnixFileRead(void *pData, void *buf, UINT size)
{
UNIXIO *p;
UINT ret;
// Validate arguments
if (pData == NULL || buf == NULL || size == 0)
{
return false;
}
p = (UNIXIO *)pData;
ret = read(p->fd, buf, size);
if (ret != size)
{
return false;
}
return true;
}
// Flush to the file
void UnixFileFlush(void *pData)
{
UNIXIO *p;
bool write_mode;
// Validate arguments
if (pData == NULL)
{
return;
}
p = (UNIXIO *)pData;
write_mode = p->write_mode;
if (write_mode)
{
fsync(p->fd);
}
}
// Close the file
void UnixFileClose(void *pData, bool no_flush)
{
UNIXIO *p;
bool write_mode;
// Validate arguments
if (pData == NULL)
{
return;
}
p = (UNIXIO *)pData;
write_mode = p->write_mode;
if (write_mode && no_flush == false)
{
fsync(p->fd);
}
close(p->fd);
UnixMemoryFree(p);
if (write_mode)
{
//sync();
}
}
// Create a file
void *UnixFileCreateW(wchar_t *name)
{
void *ret;
char *name8 = CopyUniToUtf(name);
ret = UnixFileCreate(name8);
Free(name8);
return ret;
}
void *UnixFileCreate(char *name)
{
UNIXIO *p;
int fd;
// Validate arguments
if (name == NULL)
{
return NULL;
}
fd = creat(name, 0600);
if (fd == -1)
{
return NULL;
}
// Memory allocation
p = UnixMemoryAlloc(sizeof(UNIXIO));
p->fd = fd;
p->write_mode = true;
return (void *)p;
}
// Open the file
void *UnixFileOpenW(wchar_t *name, bool write_mode, bool read_lock)
{
char *name8 = CopyUniToUtf(name);
void *ret;
ret = UnixFileOpen(name8, write_mode, read_lock);
Free(name8);
return ret;
}
void *UnixFileOpen(char *name, bool write_mode, bool read_lock)
{
UNIXIO *p;
int fd;
int mode;
// Validate arguments
if (name == NULL)
{
return NULL;
}
if (write_mode == false)
{
mode = O_RDONLY;
}
else
{
mode = O_RDWR;
}
// Open the file
fd = open(name, mode);
if (fd == -1)
{
return NULL;
}
// Memory allocation
p = UnixMemoryAlloc(sizeof(UNIXIO));
p->fd = fd;
p->write_mode = write_mode;
return (void *)p;
}
// Get UNIXIO object for stdout
void* GetUnixio4Stdout()
{
static UNIXIO unixio =
{
.fd = -1,
.write_mode = true
};
if (g_foreground)
{
unixio.fd = STDOUT_FILENO;
return &unixio;
}
return NULL;
}
// Return the current thread ID
UINT UnixThreadId()
{
UINT ret;
ret = (UINT)pthread_self();
return ret;
}
// Thread function
void *UnixDefaultThreadProc(void *param)
{
UNIXTHREAD *ut;
UNIXTHREADSTARTUPINFO *info = (UNIXTHREADSTARTUPINFO *)param;
if (info == NULL)
{
return 0;
}
ut = (UNIXTHREAD *)info->thread->pData;
// Call the thread function
info->thread_proc(info->thread, info->param);
// Set a termination flag
ut->finished = true;
// Release of reference
ReleaseThread(info->thread);
UnixMemoryFree(info);
FreeOpenSSLThreadState();
return 0;
}
// Release of thread
void UnixFreeThread(THREAD *t)
{
// Validate arguments
if (t == NULL)
{
return;
}
// Free memory
UnixMemoryFree(t->pData);
}
// Wait for the termination of the thread
bool UnixWaitThread(THREAD *t)
{
UNIXTHREAD *ut;
void *retcode = NULL;
// Validate arguments
if (t == NULL)
{
return false;
}
ut = (UNIXTHREAD *)t->pData;
if (ut == NULL)
{
return false;
}
pthread_join(ut->thread, &retcode);
return true;
}
// Thread initialization
bool UnixInitThread(THREAD *t)
{
UNIXTHREAD *ut;
UNIXTHREADSTARTUPINFO *info;
pthread_attr_t attr;
// Validate arguments
if (t == NULL || t->thread_proc == NULL)
{
return false;
}
// Thread data creation
ut = UnixMemoryAlloc(sizeof(UNIXTHREAD));
Zero(ut, sizeof(UNIXTHREAD));
// Creating the startup information
info = UnixMemoryAlloc(sizeof(UNIXTHREADSTARTUPINFO));
Zero(info, sizeof(UNIXTHREADSTARTUPINFO));
info->param = t->param;
info->thread_proc = t->thread_proc;
info->thread = t;
AddRef(t->ref);
// Thread creation
pthread_attr_init(&attr);
pthread_attr_setstacksize(&attr, UNIX_THREAD_STACK_SIZE);
t->pData = (void *)ut;
if (pthread_create(&ut->thread, &attr, UnixDefaultThreadProc, info) != 0)
{
// An error has occured
t->pData = NULL;
(void)Release(t->ref);
UnixMemoryFree(ut);
UnixMemoryFree(info);
pthread_attr_destroy(&attr);
return false;
}
pthread_attr_destroy(&attr);
return true;
}
// Release the event
void UnixFreeEvent(EVENT *event)
{
UNIXEVENT *ue = (UNIXEVENT *)event->pData;
if (ue == NULL)
{
return;
}
pthread_cond_destroy(&ue->cond);
pthread_mutex_destroy(&ue->mutex);
UnixMemoryFree(ue);
}
// Wait for a event
bool UnixWaitEvent(EVENT *event, UINT timeout)
{
UNIXEVENT *ue = (UNIXEVENT *)event->pData;
struct timeval now;
struct timespec to;
bool ret;
if (ue == NULL)
{
return false;
}
pthread_mutex_lock(&ue->mutex);
gettimeofday(&now, NULL);
to.tv_sec = now.tv_sec + timeout / 1000;
to.tv_nsec = now.tv_usec * 1000 + (timeout % 1000) * 1000 * 1000;
if ((to.tv_nsec / 1000000000) >= 1)
{
to.tv_sec += to.tv_nsec / 1000000000;
to.tv_nsec = to.tv_nsec % 1000000000;
}
ret = true;
while (ue->signal == false)
{
if (timeout != INFINITE)
{
if (pthread_cond_timedwait(&ue->cond, &ue->mutex, &to))
{
ret = false;
break;
}
}
else
{
pthread_cond_wait(&ue->cond, &ue->mutex);
}
}
ue->signal = false;
pthread_mutex_unlock(&ue->mutex);
return ret;
}
// Reset the event
void UnixResetEvent(EVENT *event)
{
UNIXEVENT *ue = (UNIXEVENT *)event->pData;
if (ue == NULL)
{
return;
}
pthread_mutex_lock(&ue->mutex);
ue->signal = false;
pthread_cond_signal(&ue->cond);
pthread_mutex_unlock(&ue->mutex);
}
// Set the event
void UnixSetEvent(EVENT *event)
{
UNIXEVENT *ue = (UNIXEVENT *)event->pData;
if (ue == NULL)
{
return;
}
pthread_mutex_lock(&ue->mutex);
ue->signal = true;
pthread_cond_signal(&ue->cond);
pthread_mutex_unlock(&ue->mutex);
}
// Initialize the event
void UnixInitEvent(EVENT *event)
{
UNIXEVENT *ue = UnixMemoryAlloc(sizeof(UNIXEVENT));
Zero(ue, sizeof(UNIXEVENT));
pthread_cond_init(&ue->cond, NULL);
pthread_mutex_init(&ue->mutex, NULL);
ue->signal = false;
event->pData = (void *)ue;
}
// Delete the lock
void UnixDeleteLock(LOCK *lock)
{
pthread_mutex_t *mutex;
// Reset Ready flag safely
UnixLock(lock);
lock->Ready = false;
UnixUnlockEx(lock, true);
// Delete the mutex
mutex = (pthread_mutex_t *)lock->pData;
pthread_mutex_destroy(mutex);
// Memory release
UnixMemoryFree(mutex);
UnixMemoryFree(lock);
}
// Unlock
void UnixUnlock(LOCK *lock)
{
UnixUnlockEx(lock, false);
}
void UnixUnlockEx(LOCK *lock, bool inner)
{
pthread_mutex_t *mutex;
if (lock->Ready == false && inner == false)
{
// State is invalid
return;
}
mutex = (pthread_mutex_t *)lock->pData;
if ((--lock->locked_count) > 0)
{
return;
}
lock->thread_id = INFINITE;
pthread_mutex_unlock(mutex);
return;
}
// Lock
bool UnixLock(LOCK *lock)
{
pthread_mutex_t *mutex;
UINT thread_id = UnixThreadId();
if (lock->Ready == false)
{
// State is invalid
return false;
}
if (lock->thread_id == thread_id)
{
lock->locked_count++;
return true;
}
mutex = (pthread_mutex_t *)lock->pData;
pthread_mutex_lock(mutex);
lock->thread_id = thread_id;
lock->locked_count++;
return true;
}
// Creating a new lock
LOCK *UnixNewLock()
{
pthread_mutex_t *mutex;
// Memory allocation
LOCK *lock = UnixMemoryAlloc(sizeof(LOCK));
// Create a mutex
mutex = UnixMemoryAlloc(sizeof(pthread_mutex_t));
// Initialization of the mutex
pthread_mutex_init(mutex, NULL);
lock->pData = (void *)mutex;
lock->Ready = true;
lock->thread_id = INFINITE;
lock->locked_count = 0;
return lock;
}
// Sleep
void UnixSleep(UINT time)
{
UINT sec = 0, millisec = 0;
// Validate arguments
if (time == 0)
{
return;
}
if (time == INFINITE)
{
// Wait forever
while (true)
{
#ifdef UNIX_SOLARIS
UnixSolarisSleep(time);
#else
sleep(1000000);
#endif
}
}
#ifdef UNIX_SOLARIS
UnixSolarisSleep(time);
#else
// Prevent overflow
sec = time / 1000;
millisec = time % 1000;
if (sec != 0)
{
sleep(sec);
}
if (millisec != 0)
{
usleep(millisec * 1000);
}
#endif
}
// Decrement
void UnixDec32(UINT *value)
{
if (value != NULL)
{
(*value)--;
}
}
// Increment
void UnixInc32(UINT *value)
{
if (value != NULL)
{
(*value)++;
}
}
// Get the System Time
void UnixGetSystemTime(SYSTEMTIME *system_time)
{
time_t now = 0;
time_64t now2 = 0;
struct tm tm;
struct timeval tv;
struct timezone tz;
// Validate arguments
if (system_time == NULL)
{
return;
}
pthread_mutex_lock(&get_time_lock);
Zero(system_time, sizeof(SYSTEMTIME));
Zero(&tv, sizeof(tv));
Zero(&tz, sizeof(tz));
time(&now);
if (sizeof(time_t) == 4)
{
now2 = (time_64t)((UINT64)((UINT32)now));
}
else
{
now2 = now;
}
c_gmtime_r(&now2, &tm);
TmToSystem(system_time, &tm);
gettimeofday(&tv, &tz);
system_time->wMilliseconds = tv.tv_usec / 1000;
pthread_mutex_unlock(&get_time_lock);
}
// Get the system timer (64bit)
UINT64 UnixGetTick64()
{
#if defined(OS_WIN32) || defined(CLOCK_REALTIME) || defined(CLOCK_MONOTONIC) || defined(CLOCK_HIGHRES)
struct timespec t;
UINT64 ret;
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)((UINT32)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