/*##############################################################################
Copyright (C) 2011 HPCC Systems.
All rights reserved. This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
############################################################################## */
// Dali Unix Server
//----------------
// to compile:
// Solaris: gcc daliservix.cpp -o daliservix -lsocket -lnsl
// or for x86: /usr/sfw/bin/gcc daliservix.cpp -o daliservix -lposix4 -lsocket -lstdc++ -lnsl
// Linux: gcc daliservix.cpp -o daliservix
#define _LARGEFILE64_SOURCE 1
#define _FILE_OFFSET_BITS 64
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
static const char *VERSTRINGBE= "DS V1.6 - Solaris";
static const char *VERSTRINGLE= "DS V1.6 - Solaris X86";
#define VERNUM 16
#define WATCHDOG_ALARM_TIME (24*60*60)
//#define _TRACE
//#define _TRACE_RW
//-------------------------------------------------------------------------------------
#ifndef size32_t // solaris hasn't defined
#define size32_t unsigned
#endif
//------------------------------------------------------------------------------------
#define BUFFER_READS
#define BUFFER_WRITES
typedef unsigned char byte;
typedef long long __int64;
enum fileBool { foundNo = false, foundYes = true, notFound = 2 };
const int endiancheck = 1;
#define is_bigendian() ((*(const char*)&endiancheck) == 0)
#define VERSTRING (is_bigendian()?VERSTRINGBE:VERSTRINGLE)
#define READ_BUFFER_SIZE (10*1048576) // 10MB
#define WRITE_BUFFER_SIZE (10*1048576) // 10MB
#define MAX_BLOCK_HEADER (READ_BUFFER_SIZE+64)
void usage()
{
printf("usage: daliservix [ ]\n\n");
printf("Default port is 7100\n");
printf("Version %d: %s\n\n",VERNUM,VERSTRING);
}
static sem_t *logsem;
void Log(const char *s)
{
char timeStamp[32];
time_t tNow;
time(&tNow);
unsigned tpid = getpid();
struct tm ltNow;
localtime_r(&tNow, <Now);
strftime(timeStamp, 32, "%m/%d/%y %H:%M:%S ", <Now);
sem_wait(logsem);
fprintf(stderr,"%s PID=%04x - %s\n",timeStamp,getpid(),s);
sem_post(logsem);
}
void LogF(const char *fmt, ...) __attribute__((format(printf, 1, 2)))
{
static char logbuf[1024*16];
va_list args;
va_start( args, fmt);
if (vsnprintf(logbuf,sizeof(logbuf)-2,fmt,args)<0)
logbuf[sizeof(logbuf)-3] = 0;
Log(logbuf);
va_end( args );
}
const char *findTail(const char *path)
{
if (!path)
return NULL;
const char *tail=path;
const char *s = path;
while (*s)
if (*(s++)=='/')
tail = s;
return tail;
}
char * makePath(char *path,const char *dir,const char *tail)
{
path[0] = 0;
unsigned l = 0;
if (dir&&(tail[0]!='/')) {
strcpy(path,dir);
l = strlen(path);
if (l && (path[l-1]!='/'))
path[l++] = '/';
}
strcpy(path+l,tail);
return path;
}
static bool WildMatchN ( const char *src, int srclen, int srcidx,
const char *pat, int patlen, int patidx,int nocase)
{
char next_char;
for (;;) {
if (patidx == patlen)
return (srcidx == srclen);
next_char = pat[patidx++];
if (next_char == '?') {
if (srcidx == srclen)
return false;
srcidx++;
}
else if (next_char != '*') {
if (nocase) {
if ((srcidx == srclen) ||
(toupper(src[srcidx])!=toupper(next_char)))
return false;
}
else
if ((srcidx == srclen) || (src[srcidx]!=next_char))
return false;
srcidx++;
}
else {
if (patidx == patlen)
return true;
while (srcidx < srclen) {
if (WildMatchN(src,srclen,srcidx,
pat, patlen, patidx,nocase))
return true;
srcidx++;
}
return false;
}
}
}
bool WildMatch(const char *src, int srclen, const char *pat, int patlen,bool nocase)
{
if (pat[0]=='*') {
// common case optimization
int i = patlen;
int j = srclen;
while (--i>0) {
if (pat[i]=='*') goto Normal;
if (j--==0) return false;
if (nocase) {
if ((toupper(pat[i])!=toupper(src[j]))&&(pat[i]!='?'))
return false;
}
else
if ((pat[i]!=src[j])&&(pat[i]!='?'))
return false;
}
return true;
}
Normal:
return WildMatchN(src,srclen,0,pat,patlen,0,nocase);
}
bool WildMatch(const char *src, const char *pat, bool nocase)
{
return WildMatch(src,strlen(src),pat,strlen(pat),nocase);
}
static unsigned long crc_32_tab[] = { /* CRC polynomial 0xedb88320 */
0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, 0x706af48fL,
0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L, 0xe0d5e91eL, 0x97d2d988L,
0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L,
0xf3b97148L, 0x84be41deL, 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L,
0x136c9856L, 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L,
0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, 0xa2677172L,
0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, 0x35b5a8faL, 0x42b2986cL,
0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L,
0x26d930acL, 0x51de003aL, 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L,
0xcfba9599L, 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L,
0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L, 0x01db7106L,
0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, 0x9fbfe4a5L, 0xe8b8d433L,
0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL,
0x91646c97L, 0xe6635c01L, 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL,
0x6c0695edL, 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L,
0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, 0xfbd44c65L,
0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, 0x4adfa541L, 0x3dd895d7L,
0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, 0x346ed9fcL, 0xad678846L, 0xda60b8d0L,
0x44042d73L, 0x33031de5L, 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL,
0xbe0b1010L, 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL,
0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, 0x2eb40d81L,
0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L, 0x03b6e20cL, 0x74b1d29aL,
0xead54739L, 0x9dd277afL, 0x04db2615L, 0x73dc1683L, 0xe3630b12L, 0x94643b84L,
0x0d6d6a3eL, 0x7a6a5aa8L, 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L,
0xf00f9344L, 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL,
0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, 0x67dd4accL,
0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, 0xd6d6a3e8L, 0xa1d1937eL,
0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL,
0xd80d2bdaL, 0xaf0a1b4cL, 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L,
0x316e8eefL, 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L,
0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL, 0xb2bd0b28L,
0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, 0x2cd99e8bL, 0x5bdeae1dL,
0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL,
0x72076785L, 0x05005713L, 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L,
0x92d28e9bL, 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L,
0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, 0x18b74777L,
0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, 0x8f659effL, 0xf862ae69L,
0x616bffd3L, 0x166ccf45L, 0xa00ae278L, 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L,
0xa7672661L, 0xd06016f7L, 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL,
0x40df0b66L, 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L,
0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, 0xcdd70693L,
0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L, 0x5d681b02L, 0x2a6f2b94L,
0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, 0x2d02ef8dL
};
#define UPDC32(octet, crc) (crc_32_tab[((crc) ^ (octet)) & 0xff] ^ ((crc) >> 8))
unsigned long crc32(const char *buf, unsigned len, unsigned long crc)
{
unsigned char c;
while(len >= 12)
{
c = *buf++; crc = UPDC32(c,crc);
c = *buf++; crc = UPDC32(c,crc);
c = *buf++; crc = UPDC32(c,crc);
c = *buf++; crc = UPDC32(c,crc);
len -= 4;
}
switch (len)
{
case 11: c = *buf++; crc = UPDC32(c,crc);
case 10: c = *buf++; crc = UPDC32(c,crc);
case 9: c = *buf++; crc = UPDC32(c,crc);
case 8: c = *buf++; crc = UPDC32(c,crc);
case 7: c = *buf++; crc = UPDC32(c,crc);
case 6: c = *buf++; crc = UPDC32(c,crc);
case 5: c = *buf++; crc = UPDC32(c,crc);
case 4: c = *buf++; crc = UPDC32(c,crc);
case 3: c = *buf++; crc = UPDC32(c,crc);
case 2: c = *buf++; crc = UPDC32(c,crc);
case 1: c = *buf++; crc = UPDC32(c,crc);
}
return(crc);
}
void processCommand(int socket);
extern "C" void sighup_callback(int signo)
{
LogF("SIGHUP(%d) received, stopping",signo);
exit(0);
}
static int activity;
extern "C" void sigalarm_callback(int signo)
{
if (signo!=0) {
LogF("SIGALRM(%d) received",signo);
if (activity==0) {
LogF("No activity since last alarm, aborting process");
exit(0);
}
}
activity = 0;
signal(SIGALRM, sigalarm_callback);
alarm(WATCHDOG_ALARM_TIME);
}
static size32_t do_pread(int fd, void *buf, size32_t count, off_t offset, int &err)
{
if (++activity==0)
activity++;
do {
int sz = (int)pread(fd,buf,count,offset);
if (sz>=0) {
err = 0;
return (size32_t)sz;
}
err = errno;
} while (err==EINTR);
return (size32_t)-1;
}
static size32_t do_pwrite(int fd, const void *buf, size32_t len, off_t offset)
{
if (++activity==0)
activity++;
int err;
do {
int sz = (int)pwrite(fd,buf,len,offset);
if (sz>=0) {
if (sz!=len) {
LogF("pwrite out of space");
}
return (size32_t)sz;
}
err = errno;
} while (err==EINTR);
LogF("pwrite errno = %d",errno);
return (size32_t)-1;
}
static size32_t do_recv(int sock, void *buf, size_t len, int flags, int &err)
{
if (++activity==0)
activity++;
do {
int sz = (int)recv(sock,buf,len,flags);
if (sz>=0) {
err = 0;
return (size32_t)sz;
}
err = errno;
} while (err==EINTR);
return (size32_t)-1;
}
int server(int port,unsigned sendbufsize,unsigned recvbufsize)
{
fprintf(stderr, "%s\n", VERSTRING);
fprintf(stderr, "Opening Dali server on port %d\n", port);
struct protoent *proto;
if ( ( proto = getprotobyname("tcp")) == NULL) {
perror("Could not get protocol number for TCP");
return -1;
}
int sockfd;
if ( (sockfd = socket(AF_INET, SOCK_STREAM, proto->p_proto)) < 0) {
perror("Could not obtain a socket");
return -1;
}
struct sockaddr_in servsock;
memset( (char *)&servsock, 0, sizeof(servsock));
servsock.sin_family = AF_INET;
servsock.sin_addr.s_addr = htonl(INADDR_ANY);
servsock.sin_port = htons(port);
if (bind(sockfd, (struct sockaddr *)&servsock, sizeof(servsock)) < 0) {
perror("Could not bind local socket\n");
return -1;
}
sem_unlink("/DALISERVIX_LOGSEM");
logsem = sem_open("/DALISERVIX_LOGSEM", O_CREAT, S_IRWXG , 1); // |S_IRWXO|S_IRWXU
if (logsem == SEM_FAILED)
perror("sem_open logsem");
LogF("Opening Dali server on port %d", (int)port);
listen(sockfd, 5);
while (1) {
struct sockaddr_in clientsock;
socklen_t clilen = sizeof(clientsock);
int newsockfd = accept(sockfd, (struct sockaddr *) &clientsock, &clilen); // blocks here
#ifdef _TRACE
LogF("accept returned sockfd %d", (int)newsockfd);
#endif
if (newsockfd < 0) {
perror("accept error");
return -1;
}
int childpid;
if ( (childpid = fork()) < 0) {
perror("fork failed");
return -1;
}
if (childpid == 0) { // the child
close(sockfd);
if ((childpid = fork()) < 0) { // forking twice to avoid zombies:
perror("fork 2 failed\n");
return -1;
}
else if (childpid > 0)
exit(0);
//sleep(1);
if (sendbufsize)
setsockopt(newsockfd, SOL_SOCKET, SO_SNDBUF, (char *) &sendbufsize, sizeof(sendbufsize));
if (recvbufsize)
setsockopt(newsockfd, SOL_SOCKET, SO_RCVBUF, (char *) &recvbufsize, sizeof(recvbufsize));
signal(SIGHUP, sighup_callback);
sigalarm_callback(0); // initialize
processCommand(newsockfd);
close(newsockfd);
#ifdef _TRACE
LogF("child with sockfd %d closing", (int)newsockfd);
#endif
exit(0);
}
close(newsockfd);
int testpid;
if ((testpid = waitpid(childpid, NULL, 0)) != childpid) {
perror("waitpid error");
return -1;
}
}
}
int main(int argc, char **argv)
{
assert(sizeof(bool)==sizeof(byte));
int port;
unsigned sendbufsize = 0;
unsigned recvbufsize = 0;
if (argc == 1)
port = 7100;
else {
port = atoi(argv[1]);
if (port==0) {
usage();
exit(-1);
}
sendbufsize = (argc>2)?(atoi(argv[2])*1024):0;
recvbufsize = (argc>3)?(atoi(argv[3])*1024):0;
}
server(port,sendbufsize,recvbufsize);
exit(0);
}
//================================================================================================================
#define MAXHANDLES 100
inline void _cpyrevn(void * _tgt, const void * _src, unsigned len)
{
char * tgt = (char *)_tgt; const char * src = (const char *)_src+len;
for (;len;len--) {
*tgt++ = *--src;
}
}
inline void _rev4(char *b) { char t=b[0]; b[0]=b[3]; b[3]=t; t=b[1]; b[1]=b[2]; b[2]=t; }
inline void BECONV(unsigned &v)
{
if (!is_bigendian())
_rev4((char *)&v);
}
inline void be_memcpy(void * _tgt, const void * _src, unsigned len)
{
if (is_bigendian())
memcpy(_tgt, _src, len);
else
_cpyrevn(_tgt, _src, len);
}
class CMemoryBuffer
{
byte * buffer;
size32_t curLen;
size32_t readPos;
size32_t maxLen;
public:
CMemoryBuffer()
{
curLen = 0;
readPos = 0;
maxLen = 1024;
buffer = (byte *)malloc(maxLen);
}
~CMemoryBuffer()
{
free(buffer);
}
size32_t length() { return curLen; }
size32_t curPos() { return readPos; }
void setLength(size32_t len)
{
assert (len<=maxLen);
curLen = len;
}
inline CMemoryBuffer & appendBigEndian(size_t len, const void * value)
{
be_memcpy(reserve(len), value, len);
return *this;
}
inline void readBigEndian(size_t len, void * value)
{
be_memcpy(value, readBlock(len), len);
}
void *reserve(size32_t sz)
{
if (sz>maxLen-curLen) {
do {
maxLen += maxLen;
} while (sz>maxLen-curLen);
buffer = (byte *)realloc(buffer,maxLen);
}
byte *ret = buffer+curLen;
curLen+=sz;
return ret;
}
CMemoryBuffer & append(fpos_t value)
{
return appendBigEndian(sizeof(value),&value);
}
CMemoryBuffer & append(unsigned value)
{
return appendBigEndian(sizeof(value),&value);
}
CMemoryBuffer & append(int value)
{
return appendBigEndian(sizeof(value),&value);
}
CMemoryBuffer & append(short value)
{
return appendBigEndian(sizeof(value),&value);
}
CMemoryBuffer & append(byte value)
{
memcpy(reserve(sizeof(value)),&value,sizeof(value));
return *this;
}
CMemoryBuffer & append(bool value)
{
memcpy(reserve(sizeof(value)),&value,1);
return *this;
}
CMemoryBuffer & append(const char *s)
{
if (!s)
append("");
else {
size32_t l=strlen(s)+1;
memcpy(reserve(l),s,l);
}
return *this;
}
void read(byte &b)
{
memcpy(&b,buffer+readPos++,1);
}
void read(fpos_t &i)
{
readBigEndian(sizeof(i),&i);
}
void read(unsigned &i)
{
readBigEndian(sizeof(i),&i);
}
void read(int &i)
{
readBigEndian(sizeof(i),&i);
}
void read(short &i)
{
readBigEndian(sizeof(i),&i);
}
void read(bool &_b)
{
byte b;
memcpy(&b,buffer+readPos,sizeof(b));
readPos+=sizeof(b);
_b = (bool)b;
}
char *readStr()
{
size32_t l = strlen((char *)buffer+readPos)+1;
char *ret = (char *)malloc(l);
memcpy(ret,buffer+readPos,l);
readPos+=l;
return ret;
}
const byte *readBlock(size32_t sz)
{
assert (sz<=maxLen-readPos);
byte *ret = buffer+readPos;
readPos+=sz;
return ret;
}
CMemoryBuffer & reset(size32_t pos=0) { readPos = pos; return *this; }
CMemoryBuffer & clear() { curLen = 0; readPos = 0; return *this; }
byte *toByteArray()
{
return buffer;
}
byte *detach()
{
byte *ret = buffer;
curLen = 0;
readPos = 0;
maxLen = 1024;
buffer = (byte *)malloc(maxLen);
return ret;
}
};
struct CDateTime
{
CDateTime()
{
year = 0;
month = 0;
day = 0;
hour = 0;
min = 0;
sec = 0;
nanosec = 0;
}
void deserialize(CMemoryBuffer &src)
{
src.read(year);
src.read(month);
src.read(day);
src.read(hour);
src.read(min);
src.read(sec);
src.read(nanosec);
}
void serialize(CMemoryBuffer &dst) const
{
dst.append(year).append(month).append(day).append(hour).append(min).append(sec).append(nanosec);
}
void setDate(unsigned _year, unsigned _month, unsigned _day)
{
year = _year;
month = _month;
day = _day;
}
void setTime(unsigned _hour, unsigned _min, unsigned _sec, unsigned _nanosec)
{
hour = _hour;
min = _min;
sec = _sec;
nanosec = _nanosec;
}
void getDate(int & _year, int & _month, int & _day) const
{
_year = year;
_month = month;
_day = day;
}
void getTime(int & _hour, int & _min, int & _sec, int & _nanosec) const
{
_hour = hour;
_min = min;
_sec = sec;
_nanosec = nanosec;
}
protected:
short year;
byte month;
byte day;
byte hour;
byte min;
byte sec;
unsigned nanosec;
};
void timetToIDateTime(CDateTime * target, time_t time)
{
if (target)
{
struct tm tm_r;
struct tm * gmt = localtime_r(&time,&tm_r);
//struct tm * gmt = gmtime(&time);
target->setDate(gmt->tm_year + 1900, gmt->tm_mon + 1, gmt->tm_mday);
target->setTime(gmt->tm_hour, gmt->tm_min, gmt->tm_sec, 0);
}
}
time_t timetFromIDateTime(const CDateTime * source)
{
if (source == NULL)
return (time_t) 0;
int bluff;
struct tm ttm;
source->getDate(ttm.tm_year, ttm.tm_mon, ttm.tm_mday);
source->getTime(ttm.tm_hour, ttm.tm_min, ttm.tm_sec, bluff);
ttm.tm_isdst = -1;
if(ttm.tm_year >= 1900)
ttm.tm_year -= 1900;
ttm.tm_mon -= 1;
time_t time = mktime(&ttm);
if (time == (time_t)-1)
time = 0;
return time;
}
bool checkDirExists(const char * filename)
{
struct stat info;
if (stat(filename, &info) != 0)
return false;
return ((info.st_mode&S_IFMT)==S_IFDIR);
}
static bool recursiveCreateDirectory(const char * path)
{
if (!path || !path[0])
return false;
if (checkDirExists(path))
return false;
if (mkdir(path,S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IWGRP|S_IXGRP|S_IROTH|S_IWOTH|S_IXOTH)==0)
return true; // mode compatible with linux setting
const char * cur = path;
if (*cur=='/')
cur++;
const char * last = NULL;
while (*cur) {
if ((*cur=='/') && cur[1])
last = cur;
cur++;
}
if (!last)
return false;
unsigned len = last-path;
char *parent = (char *)malloc(len+1);
memcpy(parent,path,len);
parent[len] = 0;
if (mkdir(path,0)!=0) {
free(parent);
return false;
}
free(parent);
return true;
}
#define throwError3(e,v,s) { LogF("ERROR: %s(%d) '%s'",#e,v,s?s:""); \
char msg[512]; \
sprintf(msg,"ERROR: %s(%d) '%s'",#e,v,s?s:""); \
reply.append(e); reply.append(msg); }
#define throwError(e) { LogF("ERROR: %s",#e); reply.append(e).append(#e); }
#define throwError2(e,v) { LogF("ERROR: %s(%d)",#e,v); \
char msg[512]; \
sprintf(msg,"ERROR: %s(%d)",#e,v); \
reply.append(e); reply.append(msg); }
void sendMemoryBuffer(int socket, CMemoryBuffer & src)
{
unsigned length = src.length() - sizeof(unsigned);
char * buffer = (char *)src.toByteArray();
be_memcpy(buffer, &length, sizeof(unsigned));
int remaining = (int)src.length();
while(1) {
int ret = send(socket, buffer, remaining,0);
#ifdef _TRACE_RW
LogF("SEND(%d)",remaining);
#endif
if (ret<0) {
perror("send failed");
exit(0);
}
remaining -= ret;
if (remaining==0)
break;
buffer += ret;
}
}
bool receiveMemoryBuffer(int socket, CMemoryBuffer & tgt)
{
unsigned oldTgtPos = tgt.length();
unsigned gotLength;
int err;
size32_t ret = do_recv(socket, (char *)&gotLength, sizeof(gotLength),MSG_WAITALL, err);
BECONV(gotLength);
if (ret!=sizeof(gotLength)) {
if (err&&(err!=ECONNRESET)) {
LogF("recv(2) failed %d",err);
exit(0);
}
close(socket);
return false;
}
if (gotLength>MAX_BLOCK_HEADER) {
LogF("invalid block length %d",gotLength);
return false;
}
byte * replyBuff = (byte *)tgt.reserve(gotLength);
ret = do_recv(socket, (char *)replyBuff, gotLength,MSG_WAITALL, err);
#ifdef _TRACE_RW
LogF("RECV(%d)",gotLength+sizeof(unsigned));
#endif
if (err!=0) {
LogF("recv(3) failed, gotLength= %d, replyBuff = %d, err = %d",gotLength,(int)replyBuff, err);
exit(0);
}
if (ret==0)
return false;
tgt.reset(oldTgtPos);
return true;
}
//---------------------------------------------------------------------------
typedef enum { IFOcreate, IFOread, IFOwrite, IFOreadwrite, IFOcreaterw } IFOmode; // modes for open
typedef enum { IFSHnone, IFSHread, IFSHwrite } IFSHmode; // sharing options.
enum {
RFCopenIO,
RFCcloseIO,
RFCread,
RFCwrite,
RFCsize,
RFCexists,
RFCremove,
RFCrename,
RFCgetver,
RFCisfile,
RFCisdirectory,
RFCisreadonly,
RFCsetreadonly,
RFCgettime,
RFCsettime,
RFCcreatedir,
RFCgetdir,
RFCstop, // not supported
RFCexec, // not supported
RFCkill, // not supported
RFCredeploy, // not supported
RFCgetcrc,
//
RFCmove,
RFCmax
};
#define RFCunlock 31 // not supported but supressed
typedef unsigned char RemoteFileCommandType;
#define ERR_REMOTE_FIRST 8200
#define ERR_REMOTE_LAST 8249
#define RFSERR_InvalidCommand 8200
#define RFSERR_NullFileIOHandle 8201
#define RFSERR_InvalidFileIOHandle 8202
#define RFSERR_TimeoutFileIOHandle 8203
#define RFSERR_OpenFailed 8204
#define RFSERR_ReadFailed 8205
#define RFSERR_WriteFailed 8206
#define RFSERR_RenameFailed 8207
#define RFSERR_SetReadOnlyFailed 8208
#define RFSERR_GetDirFailed 8209
#define RFSERR_MoveFailed 8210
#define RFEnoerror 0U
class CRemoteFileServer
{
unsigned numhandles;
protected:
typedef bool (CRemoteFileServer :: * commandFunc)(CMemoryBuffer & msg, CMemoryBuffer & reply);
public:
void registerCommand(RemoteFileCommandType cmd, commandFunc handler) { table[cmd] = handler; }
CRemoteFileServer()
{
numhandles = 0;
rbuffer = NULL;
rbufhandle = 0;
rbufbase = 0;
rbufsize = 0;
wbuffer = NULL;
wbufhandle = 0;
wbufbase = 0;
wbufsize = 0;
RemoteFileCommandType idx;
for (idx = (RemoteFileCommandType)0; idx < RFCmax; idx++)
table[idx] = &CRemoteFileServer::cmdUnknown;
registerCommand(RFCcloseIO, &CRemoteFileServer::cmdCloseFileIO);
registerCommand(RFCopenIO, &CRemoteFileServer::cmdOpenFileIO);
registerCommand(RFCread, &CRemoteFileServer::cmdRead);
registerCommand(RFCsize, &CRemoteFileServer::cmdSize);
registerCommand(RFCwrite, &CRemoteFileServer::cmdWrite);
registerCommand(RFCexists, &CRemoteFileServer::cmdExists);
registerCommand(RFCremove, &CRemoteFileServer::cmdRemove);
registerCommand(RFCrename, &CRemoteFileServer::cmdRename);
registerCommand(RFCgetver, &CRemoteFileServer::cmdGetVer);
registerCommand(RFCisfile, &CRemoteFileServer::cmdIsFile);
registerCommand(RFCisdirectory, &CRemoteFileServer::cmdIsDir);
registerCommand(RFCisreadonly, &CRemoteFileServer::cmdIsReadOnly);
registerCommand(RFCsetreadonly, &CRemoteFileServer::cmdSetReadOnly);
registerCommand(RFCgettime, &CRemoteFileServer::cmdGetTime);
registerCommand(RFCsettime, &CRemoteFileServer::cmdSetTime);
registerCommand(RFCcreatedir, &CRemoteFileServer::cmdCreateDir);
registerCommand(RFCgetdir, &CRemoteFileServer::cmdGetDir);
registerCommand(RFCgetcrc, &CRemoteFileServer::cmdGetCrc);
registerCommand(RFCmove, &CRemoteFileServer::cmdMove);
}
~CRemoteFileServer()
{
free(rbuffer);
free(wbuffer);
while (numhandles)
close(handles[--numhandles]);
}
//MORE: The file handles should timeout after a while, and accessing an old (invalid handle)
// should throw a different exception
bool checkFileIOHandle(CMemoryBuffer &reply, int handle)
{
if (handle<=0) {
throwError(RFSERR_NullFileIOHandle);
return false;
}
unsigned i;
for (i=0;i=0) {
reply.append(RFEnoerror);
reply.append(handle);
if (handle) {
assert(numhandles=rbufbase+rbufsize)
rbufhandle = 0;
else {
size32_t ofs = (size32_t)(pos-rbufbase);
size32_t cpy = rbufsize-ofs;
if (cpy>len)
cpy = len;
memcpy(data,rbuffer+ofs,cpy);
len -= cpy;
done += cpy;
if (len==0)
return done;
data = (char *)data+cpy;
pos += cpy;
}
}
rd = do_pread(handle,data,len,pos,err);
if (err!=0)
return rd;
return done+rd;
}
bool flushwrite()
{
if (wbufhandle&&(wbufsize!=0)) {
clock_t t;
size32_t numWritten = do_pwrite(wbufhandle, wbuffer, wbufsize, wbufbase);
wbufbase += wbufsize;
if (numWritten!=wbufsize) {
wbufsize = 0;
return false;
}
wbufsize = 0;
}
return true;
}
size32_t dowrite(int handle, const byte *data, size32_t len, fpos_t pos)
{
size32_t done=0;
while (len) {
if (handle!=wbufhandle) {
if (wbufhandle!=0) // only one handle used per process but in case not
return do_pwrite(handle, data, len, pos);
wbufhandle = handle;
if (!wbuffer)
wbuffer = (char *)malloc(WRITE_BUFFER_SIZE);
wbufbase = pos;
wbufsize = 0;
}
if ((pos!=wbufbase+wbufsize)||(len+wbufsize>WRITE_BUFFER_SIZE)) {
if (!flushwrite())
return (size32_t)-1;
wbufbase = pos;
}
size32_t tocopy = len;
if (tocopy>WRITE_BUFFER_SIZE)
tocopy = WRITE_BUFFER_SIZE;
len -= tocopy;
memcpy(wbuffer+wbufsize,data,tocopy);
data = ((byte *)data)+tocopy;
wbufsize += tocopy;
pos += tocopy;
done += tocopy;
}
return done;
}
bool cmdRead(CMemoryBuffer & msg, CMemoryBuffer & reply)
{
fpos_t pos;
size32_t len;
int handle = readFileIOHandle(msg,reply);
if (handle<0)
return false;
msg.read(pos);
msg.read(len);
//arrange it so we read directly into the reply buffer...
unsigned posOfErr = reply.length();
reply.append((unsigned)RFEnoerror);
size32_t numRead;
unsigned posOfLength = reply.length();
reply.reserve(sizeof(numRead));
void * data = reply.reserve(len);
int err;
#ifdef BUFFER_READS
numRead = do_buf_read(handle, data, len, pos, err);
#else
numRead = do_pread(handle, data, len, pos, err);
#endif
#ifdef _TRACE
LogF("read file, handle = %d, pos = %lld, toread = %d, read = %d",handle,pos,len,numRead);
#endif
if (numRead==(size32_t)-1) {
reply.setLength(posOfErr);
throwError2(RFSERR_ReadFailed,err);
return false;
}
be_memcpy((char *)reply.toByteArray()+posOfLength,&numRead,sizeof(numRead));
reply.setLength(posOfLength+sizeof(numRead)+numRead);
return true;
}
bool cmdSize(CMemoryBuffer & msg, CMemoryBuffer & reply)
{
int handle = readFileIOHandle(msg,reply);
if (handle<0)
return false;
#ifdef BUFFER_WRITES
if (wbufhandle==handle) {
if (!flushwrite()) {
throwError(RFSERR_WriteFailed);
return false;
}
wbufhandle = NULL;
}
#endif
fpos_t size = lseek(handle,0,SEEK_END); // we don't use seek pos so no need to restore
reply.append((unsigned)RFEnoerror).append(size);
#ifdef _TRACE
LogF("size file, handle = %d, size = %lld",handle,size);
#endif
return true;
}
bool cmdWrite(CMemoryBuffer & msg, CMemoryBuffer & reply)
{
fpos_t pos;
size32_t len;
const byte * data;
int handle = readFileIOHandle(msg,reply);
if (handle<0)
return false;
msg.read(pos);
msg.read(len);
data = msg.readBlock(len);
#ifdef BUFFER_WRITES
size32_t numWritten = dowrite(handle, data, len, pos);
#else
size32_t numWritten = do_pwrite(handle, data, len, pos);
#endif
#ifdef _TRACE
LogF("write file, handle = %d, towrite = %d, written = %d",handle,len,numWritten);
#endif
if (numWritten==(size32_t)-1) {
throwError(RFSERR_WriteFailed);
return false;
}
reply.append((unsigned)RFEnoerror).append(numWritten);
return true;
}
bool cmdExists(CMemoryBuffer & msg, CMemoryBuffer & reply)
{
char *filename = msg.readStr();
#ifdef _TRACE
LogF("exists, '%s'",filename);
#endif
struct stat s;
reply.append((unsigned)RFEnoerror).append((bool)(stat(filename,&s)==0));
free(filename);
return true;
}
bool cmdRemove(CMemoryBuffer & msg, CMemoryBuffer & reply)
{
char *filename = msg.readStr();
#ifdef _TRACE
LogF("remove, '%s'",filename);
#endif
reply.append((unsigned)RFEnoerror).append((bool)(unlink(filename)==0));
free(filename);
return true;
}
bool cmdRename(CMemoryBuffer & msg, CMemoryBuffer & reply)
{
char *from = msg.readStr();
char *to = msg.readStr();
const char *totail = findTail(to);
if (totail==to) { // kludge
const char *fromtail = findTail(from);
if (fromtail!=from) {
unsigned l = fromtail-from;
char * s = (char *)malloc(strlen(to)+l+1);
memcpy(s,from,l);
strcpy(s+l,to);
free(to);
to = s;
}
}
#ifdef _TRACE
LogF("rename, '%s' to '%s'",from,to);
#endif
if (rename(from,to)!=0) {
throwError(RFSERR_RenameFailed);
free(from);
free(to);
return false;
}
reply.append((unsigned)RFEnoerror);
free(from);
free(to);
return true;
}
bool cmdUnknown(CMemoryBuffer & msg, CMemoryBuffer & reply)
{
RemoteFileCommandType cmd;
msg.reset();
msg.read(cmd);
if (cmd!=RFCunlock) {
throwError2(RFSERR_InvalidCommand, cmd);
}
else { // kludge - don't log if unlock
char msg[512];
sprintf(msg,"ERROR: RFSERR_InvalidCommand (%d)",cmd);
reply.append(RFSERR_InvalidCommand); reply.append(msg);
}
return false;
}
bool cmdGetVer(CMemoryBuffer & msg, CMemoryBuffer & reply)
{
if (msg.length()-msg.curPos()>sizeof(unsigned))
reply.append((unsigned)RFEnoerror).append(VERSTRING);
else
reply.append((unsigned)0x10000+VERNUM).append(VERSTRING);
return true;
}
bool cmdIsFile(CMemoryBuffer &msg, CMemoryBuffer &reply)
{
char *filename = msg.readStr();
#ifdef _TRACE
LogF("isFile, '%s'",filename);
#endif
struct stat s;
unsigned ret;
if (stat(filename, &s) != 0)
ret = (unsigned)notFound;
else
ret = (unsigned)(((s.st_mode&S_IFMT)==S_IFREG) ? foundYes : foundNo);
reply.append((unsigned)RFEnoerror).append(ret);
free(filename);
return true;
}
bool cmdIsDir(CMemoryBuffer &msg, CMemoryBuffer &reply)
{
char *filename = msg.readStr();
#ifdef _TRACE
LogF("isDir, '%s'",filename);
#endif
struct stat s;
unsigned ret;
if (stat(filename, &s) != 0)
ret = (unsigned)notFound;
else
ret = (unsigned)(((s.st_mode&S_IFMT)==S_IFDIR) ? foundYes : foundNo);
reply.append((unsigned)RFEnoerror).append(ret);
free(filename);
return true;
}
bool cmdIsReadOnly(CMemoryBuffer &msg, CMemoryBuffer &reply)
{
char *filename = msg.readStr();
#ifdef _TRACE
LogF("isReadOnly, '%s'",filename);
#endif
struct stat s;
unsigned ret;
if (stat(filename, &s) != 0)
ret = (unsigned)notFound;
else
ret = (s.st_mode & (S_IWUSR|S_IWGRP|S_IWOTH)) ? foundNo : foundYes;
// I don't think this is necessarily correct but consistant with linux implementation
reply.append((unsigned)RFEnoerror).append(ret);
free(filename);
return true;
}
bool cmdSetReadOnly(CMemoryBuffer &msg, CMemoryBuffer &reply)
{
char *filename = msg.readStr();
bool set;
msg.read(set);
#ifdef _TRACE
LogF("setReadOnly, '%s'",filename);
#endif
struct stat s;
unsigned ret;
if (stat(filename, &s) != 0) {
throwError(RFSERR_SetReadOnlyFailed);
free(filename);
return false;
}
// not sure correct but consistant with isReadOnly
if (set)
s.st_mode &= ~(S_IWUSR|S_IWGRP|S_IWOTH);
else
s.st_mode |= (S_IWUSR|S_IWGRP|S_IWOTH);
chmod(filename, s.st_mode);
reply.append((unsigned)RFEnoerror);
free(filename);
return true;
}
bool cmdGetTime(CMemoryBuffer &msg, CMemoryBuffer &reply)
{
char *filename = msg.readStr();
#ifdef _TRACE
LogF("getTime, '%s'",filename);
#endif
CDateTime createTime;
CDateTime modifiedTime;
CDateTime accessedTime;
struct stat info;
if (stat(filename, &info) != 0) {
reply.append((unsigned)RFEnoerror).append((bool)false);
free(filename);
return true;
}
timetToIDateTime(&accessedTime, info.st_atime);
timetToIDateTime(&createTime, info.st_ctime);
timetToIDateTime(&modifiedTime, info.st_mtime);
reply.append((unsigned)RFEnoerror).append((bool)true);
createTime.serialize(reply);
modifiedTime.serialize(reply);
accessedTime.serialize(reply);
free(filename);
return true;
}
bool cmdSetTime(CMemoryBuffer &msg, CMemoryBuffer &reply)
{
char *filename = msg.readStr();
bool creategot;
CDateTime createTime;
bool modifiedgot;
CDateTime modifiedTime;
bool accessedgot;
CDateTime accessedTime;
msg.read(creategot);
if (creategot)
createTime.deserialize(msg);
msg.read(modifiedgot);
if (modifiedgot)
modifiedTime.deserialize(msg);
msg.read(accessedgot);
if (accessedgot)
accessedTime.deserialize(msg);
#ifdef _TRACE
LogF("setTime, '%s' %d",filename);
#endif
struct utimbuf am;
if (!accessedgot||!modifiedgot) {
struct stat info;
if (stat(filename, &info) != 0) {
reply.append((unsigned)RFEnoerror).append((bool)false);
free(filename);
return true;
}
am.actime = info.st_atime;
am.modtime = info.st_mtime;
}
if (accessedgot)
am.actime = timetFromIDateTime (&accessedTime);
if (modifiedgot)
am.modtime = timetFromIDateTime (&modifiedTime);
if(utime(filename, &am)!=0)
reply.append((unsigned)RFEnoerror).append((bool)false);
else
reply.append((unsigned)RFEnoerror).append((bool)true);
free(filename);
return true;
}
bool cmdCreateDir(CMemoryBuffer &msg, CMemoryBuffer &reply)
{
char *path = msg.readStr();
#ifdef _TRACE
LogF("createDir, '%s'",path);
#endif
if (*path&&recursiveCreateDirectory(path))
reply.append((unsigned)RFEnoerror).append((bool)true);
else
reply.append((unsigned)RFEnoerror).append((bool)false);
free(path);
return true;
}
bool cmdGetDir(CMemoryBuffer &msg, CMemoryBuffer &reply)
{
char *path = msg.readStr();
char *wildcard = msg.readStr();
bool includedir;
bool sub;
msg.read(includedir);
msg.read(sub);
#ifdef _TRACE
LogF("getDir, '%s' '%s'",path,wildcard);
#endif
DIR * handle = opendir(path);
if (!handle) {
throwError(RFSERR_GetDirFailed);
free(path);
free(wildcard);
return false;
}
reply.append((unsigned)RFEnoerror);
byte b=1;
struct dirent *entry;
while(1) {
entry = readdir(handle);
// need better checking here?
if (!entry)
break;
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
continue;
struct stat st;
char fullname[512];
if (stat(makePath(fullname,path,entry->d_name), &st) != 0)
continue;
bool curisdir = false;
if ((st.st_mode&S_IFMT)==S_IFDIR) {
// sub TBD
if (!includedir)
continue;
curisdir = true;
}
if (!*wildcard || WildMatch(entry->d_name, wildcard, false)) {
reply.append(b);
reply.append(curisdir);
fpos_t sz = curisdir?0:st.st_size;
CDateTime dt;
memset(&dt,0,sizeof(dt));
char fullname[512];
timetToIDateTime(&dt,st.st_mtime);
reply.append(sz);
dt.serialize(reply);
reply.append(entry->d_name);
}
}
closedir(handle);
b = 0;
reply.append(b);
free(path);
free(wildcard);
}
bool cmdGetCrc(CMemoryBuffer &msg, CMemoryBuffer &reply)
{
char *filename = msg.readStr();
fpos_t pos=0;
fpos_t len=0x7fffffffffffffffLL;
//msg.read(pos); // pos/len not supported yet
//msg.read(len);
#ifdef _TRACE
LogF("getCrc, '%s' %d %d",filename,from,len);
#endif
int handle = open(filename, O_RDONLY);
if (handle<0) {
throwError3(RFSERR_OpenFailed,errno,filename);
free(filename);
return false;
}
void *buf = malloc(READ_BUFFER_SIZE);
unsigned long crc=~0;
while (len!=0) {
size32_t sz = (len>READ_BUFFER_SIZE)?READ_BUFFER_SIZE:((size32_t)len);
int err;
sz = do_pread(handle,buf,sz,pos,err);
if (err!=0) {
throwError3(RFSERR_ReadFailed,err,filename);
free(filename);
return false;
}
if (sz==0)
len = 0;
else
crc = crc32((const char *)buf,sz,crc);
len -= sz;
pos += sz;
}
close(handle);
free(buf);
reply.append((unsigned)RFEnoerror).append((unsigned)(~crc));
free(filename);
return true;
}
bool cmdMove(CMemoryBuffer &msg, CMemoryBuffer &reply)
{
char *from = msg.readStr();
char *to = msg.readStr();
#ifdef _TRACE
LogF("move, '%s' %s",from,to);
#endif
int err = rename(from,to);
if (err<0) {
throwError3(RFSERR_MoveFailed,errno,from);
free(from);
free(to);
return false;
}
reply.append((unsigned)RFEnoerror);
free(from);
free(to);
return true;
}
bool dispatchCommand(CMemoryBuffer & msg, CMemoryBuffer & reply)
{
RemoteFileCommandType cmd;
msg.read(cmd);
if (cmd < RFCmax)
return (this->*(table[cmd]))(msg, reply);
return cmdUnknown(msg,reply);
}
void processCommand(int socket)
{
char retname[256];
struct sockaddr_in *nameptr;
struct sockaddr_in name;
socklen_t namelen = sizeof(name);
nameptr = &name;
if(getpeername(socket,(struct sockaddr*)&name, &namelen)<0) {
LogF("getpeername failed %d",errno);
}
else {
strncpy(retname,inet_ntoa(nameptr->sin_addr),sizeof(retname));
LogF("Connected to %s",retname);
}
CMemoryBuffer replyBuffer;
CMemoryBuffer commandBuffer;
while (receiveMemoryBuffer(socket, commandBuffer.clear())) {
dispatchCommand(commandBuffer, replyBuffer.clear().append((unsigned)0)); // reserve space for length prefix
sendMemoryBuffer(socket, replyBuffer);
}
}
int readFileIOHandle(CMemoryBuffer & msg, CMemoryBuffer & reply)
{
int handle;
msg.read(handle);
if (!checkFileIOHandle(reply,handle))
return -1;
return handle;
}
protected:
commandFunc table[RFCmax];
int handles[MAXHANDLES];
char * rbuffer;
int rbufhandle;
fpos_t rbufbase;
size32_t rbufsize;
char * wbuffer;
int wbufhandle;
fpos_t wbufbase;
size32_t wbufsize;
};
void processCommand(int socket)
{
CRemoteFileServer server;
server.processCommand(socket);
}