mirror of
https://github.com/SoftEtherVPN/SoftEtherVPN.git
synced 2025-04-03 18:00:08 +03:00
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.
2854 lines
52 KiB
C
Executable File
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
|