From 99340c43f64ab2633f2f0203f4ce6609fdc5ee82 Mon Sep 17 00:00:00 2001 From: user Date: Thu, 12 Mar 2009 15:23:17 +0000 Subject: [PATCH] first import of old cilux linux platform code --- bin/make.sh | 6 + makefile | 84 ++ modules/mid/tnl.frag | 11 + modules/mid/tnl.vert | 20 + src/drivers/mid/mid.c | 574 ++++++++++++ src/drivers/np/cache.c | 182 ++++ src/drivers/np/np.c | 562 +++++++++++ src/drivers/np/uri2chan.c | 294 ++++++ src/include/kernelapi.h | 218 +++++ src/include/ni.h | 77 ++ src/ni/headers.c | 523 +++++++++++ src/ni/ni.c | 783 ++++++++++++++++ src/platform/container.h | 19 + src/platform/kernelapi.c | 2053 +++++++++++++++++++++++++++++++++++++++++ src/platform/linux/cilux.c | 73 ++ src/platform/linux/osapi.c | 275 ++++++ src/platform/linux/osapi.h | 58 ++ src/platform/linux/platform.h | 27 + 18 files changed, 5839 insertions(+) create mode 100755 bin/make.sh create mode 100644 makefile create mode 100644 modules/mid/tnl.frag create mode 100644 modules/mid/tnl.vert create mode 100644 src/drivers/mid/mid.c create mode 100644 src/drivers/np/cache.c create mode 100644 src/drivers/np/np.c create mode 100644 src/drivers/np/uri2chan.c create mode 100644 src/include/kernelapi.h create mode 100644 src/include/ni.h create mode 100644 src/ni/headers.c create mode 100644 src/ni/ni.c create mode 100644 src/platform/container.h create mode 100644 src/platform/kernelapi.c create mode 100644 src/platform/linux/cilux.c create mode 100644 src/platform/linux/osapi.c create mode 100644 src/platform/linux/osapi.h create mode 100644 src/platform/linux/platform.h diff --git a/bin/make.sh b/bin/make.sh new file mode 100755 index 0000000..b00a095 --- /dev/null +++ b/bin/make.sh @@ -0,0 +1,6 @@ +#!/bin/sh + +pkill cilux +make clean linux 2>&1 | tee out.log +egrep -i warn out.log + diff --git a/makefile b/makefile new file mode 100644 index 0000000..95ec4e2 --- /dev/null +++ b/makefile @@ -0,0 +1,84 @@ + +# ---------------------------------------------------------------------------- # + +CILUX_VERSION=0.3.AA + +# ---------------------------------------------------------------------------- # + +all: linux + +linux: CC=/usr/bin/gcc +linux: CCLIB=/usr/bin/gcc -shared -Wl,-soname +linux: STRIP=strip +linux: STRIP=ls -l +linux: CCOPTIONS=-g -O -Wall -Wimplicit +linux: COMPILEOPTIONS=-march=i386 -mtune=i586 -fPIC +linux: LINKOPTIONS=-Wl,-export-dynamic -Wl,-rpath,. +linux: LINKOPTIONS=-Wl,-export-dynamic +linux: INCLUDES=-I/usr/local/include -I../include -I../../include -I.. -I../platform/linux -I../../platform/linux +linux: LIBRARIES=-L/usr/local/lib -lnsl -ldl -lm -lX11 -lEGL -lGLESv2 +linux: linux-all + +linux-all: cilux mod-ni.so mod-np.so mod-mid.so install-lin + +# ---------------------------------------------------------------------------- # + +version-lin: + @echo '$@' + @rm -f src/platform/linux/version.h + @echo -n "static char* cilux_version=\"Cilux " > src/platform/linux/version.h + @echo -n $(CILUX_VERSION) >> src/platform/linux/version.h + @echo -n " Linux (Built " >> src/platform/linux/version.h + @date | tr '\012' ')' >> src/platform/linux/version.h + @echo "\";" >> src/platform/linux/version.h + @echo "static char* cilux_ciux=\"np\";" >> src/platform/linux/version.h + +cilux: version-lin src/platform/linux/cilux.c src/platform/linux/osapi.c src/platform/kernelapi.c src/include/kernelapi.h src/platform/linux/platform.h + (cd src/platform/linux; $(CC) $(CCOPTIONS) -c cilux.c -o cilux.o $(COMPILEOPTIONS) $(INCLUDES)) + (cd src/platform; $(CC) $(CCOPTIONS) -c kernelapi.c -o kernelapi.o $(COMPILEOPTIONS) $(INCLUDES)) + (cd src/platform/linux; $(CC) $(CCOPTIONS) -o ../../../cilux cilux.o ../kernelapi.o $(LINKOPTIONS) $(LIBRARIES)) + $(STRIP) cilux + @echo '--------------------' + +mod-ni.so: src/ni/ni.c src/ni/headers.c src/include/ni.h src/include/kernelapi.h src/platform/linux/platform.h + (cd src/ni; $(CC) $(CCOPTIONS) -c headers.c -o headers.o $(COMPILEOPTIONS) $(INCLUDES)) + (cd src/ni; $(CC) $(CCOPTIONS) -c ni.c -o ni.o $(COMPILEOPTIONS) $(INCLUDES)) + $(CCLIB),mod-ni.so -o mod-ni.so src/ni/headers.o src/ni/ni.o + $(STRIP) mod-ni.so + @echo '--------------------' + +mod-np.so: src/drivers/np/np.c src/drivers/np/uri2chan.c src/include/kernelapi.h src/platform/linux/platform.h + (cd src/drivers/np; $(CC) $(CCOPTIONS) -c np.c -o np.o $(COMPILEOPTIONS) $(INCLUDES)) + (cd src/drivers/np; $(CC) $(CCOPTIONS) -c uri2chan.c -o uri2chan.o $(COMPILEOPTIONS) $(INCLUDES)) + (cd src/drivers/np; $(CC) $(CCOPTIONS) -c cache.c -o cache.o $(COMPILEOPTIONS) $(INCLUDES)) + $(CCLIB),mod-np.so -o mod-np.so src/drivers/np/np.o src/drivers/np/uri2chan.o src/drivers/np/cache.o + $(STRIP) mod-np.so + @echo '--------------------' + +mod-mid.so: src/drivers/mid/mid.c src/include/kernelapi.h src/platform/linux/platform.h + (cd src/drivers/mid; $(CC) $(CCOPTIONS) -c mid.c -o mid.o $(COMPILEOPTIONS) $(INCLUDES)) + $(CCLIB),mod-mid.so -o mod-mid.so src/drivers/mid/mid.o + $(STRIP) mod-mid.so + @echo '--------------------' + +install-lin: + cp cilux /usr/local/bin + cp mod-ni.so mod-np.so /usr/local/lib + cp mod-mid.so modules/mid + @echo '--------------------' + +# ---------------------------------------------------------------------------- # + +clean: + find . -name '*.o' | xargs rm -f + find . -name '*.log' | xargs rm -f + rm -f cilux *.so + rm -f ,* + rm -f modules/*/mod-*.* + rm -f src/platform/*/version.h + @echo '--------------------' + +# ---------------------------------------------------------------------------- # + + + diff --git a/modules/mid/tnl.frag b/modules/mid/tnl.frag new file mode 100644 index 0000000..30cef92 --- /dev/null +++ b/modules/mid/tnl.frag @@ -0,0 +1,11 @@ + +uniform sampler2D texture; + +varying mediump float varyLight; +varying mediump vec2 varyTexCoord; + +void main (void) +{ + gl_FragColor = varyLight * texture2D(texture, varyTexCoord); +} + diff --git a/modules/mid/tnl.vert b/modules/mid/tnl.vert new file mode 100644 index 0000000..7c5dbdd --- /dev/null +++ b/modules/mid/tnl.vert @@ -0,0 +1,20 @@ + +uniform mediump vec3 frameLightDirection; +uniform mediump mat4 frameTRSV; +uniform mediump mat4 frameMVP; +uniform mediump mat3 frameTRSN; + +attribute highp vec4 vertPos; +attribute mediump vec3 vertNormal; +attribute mediump vec4 vertTexCoord; + +varying mediump float varyLight; +varying mediump vec2 varyTexCoord; + +void main(void) +{ + gl_Position = vertPos * frameTRSV * frameMVP; + varyLight = max( dot( vertNormal * frameTRSN, frameLightDirection ), 0.0 ) + 0.4; + varyTexCoord = vertTexCoord.st; +} + diff --git a/src/drivers/mid/mid.c b/src/drivers/mid/mid.c new file mode 100644 index 0000000..c599675 --- /dev/null +++ b/src/drivers/mid/mid.c @@ -0,0 +1,574 @@ + + +/* -------------------------------------------------------------------------- */ + +#include +#include + +#include + +/* ------------------------------------------------------------- */ + +#include +#include +#include + +#include "X11/Xutil.h" + +#include +#include + +#define WINDOW_WIDTH 640 +#define WINDOW_HEIGHT 480 + +#define POS_ARRAY 0 +#define NORMAL_ARRAY 1 +#define TEXCOORD_ARRAY 2 + +#define TEX_SIZE 128 + +/* ------------------------------------------------------------- */ + +Window x11Window = 0; +Display* x11Display = 0; +long x11Screen = 0; +XVisualInfo* x11Visual = 0; +Colormap x11Colormap = 0; + +EGLDisplay eglDisplay = 0; +EGLConfig eglConfig = 0; +EGLSurface eglSurface = 0; +EGLContext eglContext = 0; + +/* ------------------------------------------------------------- */ + +/* -------------------------------------------------------------------------- */ + +#define GLfloat float +static GLfloat xco= 0; +static GLfloat yco= 1; +static GLfloat zco= -35; +static GLfloat view_rotx=0.0, view_roty=0.0, view_rotz=0.0; +static int shift=0; + +/* -------------------------------------------------------------------------- */ + +static int handles_resource(char* name); +static void sync_resource(ni_resource* res); +static void init_gl(void); +static void make_world(void); +static void reshape(int width, int height); +static void draw(void); +static void key(unsigned char k, int down); +static void plane(void* n); +static void double_cube(void* s); +static void cube(GLfloat size, int outside); +static int set_object(char* n, + GLfloat x, GLfloat y, GLfloat z, GLfloat angle, + void (*listFn)(void*), + void* listFnArgs ); + +/* -------------------------------------------------------------------------- */ + +EXPORT int mid_module_loaded(void) +{ + ni_register_driver("mid", handles_resource, sync_resource); + + init_gl(); + make_world(); + + k_gl_register_reshape(reshape); + k_gl_register_draw(draw); + k_gl_register_key(key); + + k_log_out("MID Driver initialised"); + + testloop(); + + return 1; +} + +EXPORT int mid_module_event(void* data) +{ + k_log_out("MID got event: %p", data); + ni_event* evt=data; + ni_event_delete(evt); + return 1; +} + +/* -------------------------------------------------------------------------- */ + +int handles_resource(char* name) +{ + return 0; +} + +void sync_resource(ni_resource* res) +{ +} + +/* -------------------------------------------------------------------------- */ + +void init_gl(void) +{ +} + +void make_world(void) +{ +} + +void reshape(int width, int height) +{ +} + +void draw(void) +{ +} + +#define SHIFT 0 +void key(unsigned char k, int down) +{ + if(k==SHIFT && down){ shift=1; return; } + if(k==SHIFT && !down){ shift=0; return; } + if(!down) return; + + if(shift) k-=('a'-'A'); + + float speed=0.25; + switch (k) { + case 'H': + xco-=speed*(float)sin((view_roty-90)*3.14/180); + zco+=speed*(float)cos((view_roty-90)*3.14/180); + if(xco< -35) xco= -35; + if(xco> 35) xco= 35; + if(zco< -35) zco= -35; + if(zco> 35) zco= 35; + break; + case 'L': + xco+=speed*(float)sin((view_roty-90)*3.14/180); + zco-=speed*(float)cos((view_roty-90)*3.14/180); + if(xco< -35) xco= -35; + if(xco> 35) xco= 35; + if(zco< -35) zco= -35; + if(zco> 35) zco= 35; + break; + case 'i': + xco-=speed*(float)sin(view_roty*3.14/180); + zco+=speed*(float)cos(view_roty*3.14/180); + if(xco< -35) xco= -35; + if(xco> 35) xco= 35; + if(zco< -35) zco= -35; + if(zco> 35) zco= 35; + break; + case 'o': + xco+=speed*(float)sin(view_roty*3.14/180); + zco-=speed*(float)cos(view_roty*3.14/180); + if(xco< -35) xco= -35; + if(xco> 35) xco= 35; + if(zco< -35) zco= -35; + if(zco> 35) zco= 35; + break; + case 'j': + yco-=speed; + if(yco< 0.2) yco= 0.2; + break; + case 'k': + yco+=speed; + break; + case 'l': + view_roty += speed*20; + break; + case 'h': + view_roty -= speed*20; + break; +/* + case 'J': + view_rotx += 2.0; + break; + case 'K': + view_rotx -= 2.0; + break; + case 'z': + view_rotz += 2.0; + break; + case 'Z': + view_rotz -= 2.0; + break; +*/ + default: + return; + } + draw(); +} + +/* -------------------------------------------------------------------------- */ +/* ------------------------------------------------------------- */ + +void cleanupAndExit(int code) +{ + eglMakeCurrent(eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + eglTerminate(eglDisplay); + + if(x11Window) XDestroyWindow(x11Display, x11Window); + if(x11Colormap) XFreeColormap( x11Display, x11Colormap); + if(x11Display) XCloseDisplay( x11Display); + + exit(code); +} + +/* ------------------------------------------------------------- */ + +void getX11Display(int windowwidth, int windowheight) +{ + x11Display = XOpenDisplay(0); + + if(!x11Display) { + printf("Error: Unable to open X display\n"); + cleanupAndExit(-1); + } + + x11Screen = XDefaultScreen(x11Display); + Window rootWindow = RootWindow(x11Display, x11Screen); + int depth = DefaultDepth(x11Display, x11Screen); + x11Visual = malloc(sizeof(XVisualInfo)); + XMatchVisualInfo(x11Display, x11Screen, depth, TrueColor, x11Visual); + + if(!x11Visual) { + printf("Error: Unable to acquire visual\n"); + cleanupAndExit(-1); + } + + x11Colormap = XCreateColormap(x11Display, rootWindow, x11Visual->visual, AllocNone); + XSetWindowAttributes XSWA; + XSWA.colormap = x11Colormap; + XSWA.event_mask = StructureNotifyMask | ExposureMask | ButtonPressMask | ButtonReleaseMask | KeyPressMask | KeyReleaseMask; + unsigned int cwmask = CWBackPixel | CWBorderPixel | CWEventMask | CWColormap; + + x11Window = XCreateWindow(x11Display, RootWindow(x11Display, x11Screen), 0, 0, windowwidth, windowheight, + 0, CopyFromParent, InputOutput, CopyFromParent, cwmask, &XSWA); + XMapWindow(x11Display, x11Window); + XFlush(x11Display); +} + +int isEGLError(char* where) +{ + EGLint err = eglGetError(); + if(err != EGL_SUCCESS) { + printf("EGL failed at %s (%d).\n", where, err); + return 1; + } + return 0; +} + +void setUpEGL(void) +{ + eglDisplay = eglGetDisplay((EGLNativeDisplayType)x11Display); + + EGLint iMajorVersion, iMinorVersion; + if(!eglInitialize(eglDisplay, &iMajorVersion, &iMinorVersion)) { + printf("Error: eglInitialize() failed.\n"); + cleanupAndExit( -1); + } + + EGLint pi32ConfigAttribs[5]; + pi32ConfigAttribs[0] = EGL_SURFACE_TYPE; + pi32ConfigAttribs[1] = EGL_WINDOW_BIT; + pi32ConfigAttribs[2] = EGL_RENDERABLE_TYPE; + pi32ConfigAttribs[3] = EGL_OPENGL_ES2_BIT; + pi32ConfigAttribs[4] = EGL_NONE; + + EGLint pi32ContextAttribs[3]; + pi32ContextAttribs[0] = EGL_CONTEXT_CLIENT_VERSION; + pi32ContextAttribs[1] = 2; + pi32ContextAttribs[2] = EGL_NONE; + + int iConfigs; + if(!eglChooseConfig(eglDisplay, pi32ConfigAttribs, &eglConfig, 1, &iConfigs) || (iConfigs != 1)) { + printf("Error: eglChooseConfig() failed.\n"); + cleanupAndExit( -1); + } + + eglSurface = eglCreateWindowSurface(eglDisplay, eglConfig, (EGLNativeWindowType)x11Window, NULL); + + if(isEGLError("eglCreateWindowSurface")) cleanupAndExit( -1); + + eglContext = eglCreateContext(eglDisplay, eglConfig, NULL, pi32ContextAttribs); + + if(isEGLError("eglCreateContext")) cleanupAndExit( -1); + + eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext); + + if(isEGLError("eglMakeCurrent")) cleanupAndExit( -1); +} + +/* ------------------------------------------------------------- */ + +GLuint isShaderError(GLuint thing) +{ + GLint isShader = glIsShader(thing); + + GLint ok; + if(isShader){ + glGetShaderiv(thing, GL_COMPILE_STATUS, &ok); + }else{ + glGetProgramiv(thing, GL_LINK_STATUS, &ok); + } + if(ok) return 0; + + GLint infoLen=0; + if(isShader){ + glGetShaderiv(thing, GL_INFO_LOG_LENGTH, &infoLen); + }else{ + glGetProgramiv(thing, GL_INFO_LOG_LENGTH, &infoLen); + } + if(infoLen){ + char* infoLog = malloc(sizeof(char)*infoLen); + if(isShader){ + glGetShaderInfoLog(thing, infoLen, NULL, infoLog); + }else{ + glGetProgramInfoLog(thing, infoLen, NULL, infoLog); + } + printf("%s: %s\n", isShader? "Shader compile": "Program link", infoLog); + free(infoLog); + } + return 1; +} + +char* file2string(const char *path) +{ + FILE *fd; + long len, r; + char *str; + + if(!(fd=fopen(path, "r"))) { + fprintf(stderr, "Can't open file '%s' for reading\n", path); + return NULL; + } + + fseek(fd, 0, SEEK_END); + len = ftell(fd); + fseek(fd, 0, SEEK_SET); + + if(!(str=malloc(len*sizeof(char)))) { + fprintf(stderr, "Can't malloc space for '%s'\n", path); + return NULL; + } + + r = fread(str, sizeof(char), len, fd); + + str[r-1] = '\0'; + + fclose(fd); + + return str; +} + +/* ------------------------------------------------------------- */ + +GLuint program; +GLuint texture; +GLuint vbo; +unsigned int numberOfVertices; +unsigned int posStep; +unsigned int normStep; +unsigned int tcStep; +unsigned int stride; +float angle=0.0; +float viewAngle = 0.0; + +GLuint useTheProgram() +{ + const char *vsSource = file2string("tnl.vert"); + const char *fsSource = file2string("tnl.frag"); + + GLuint vs = glCreateShader(GL_VERTEX_SHADER); + glShaderSource(vs, 1, &vsSource, NULL); + glCompileShader(vs); + if(isShaderError(vs)) return 0; + + GLuint fs = glCreateShader(GL_FRAGMENT_SHADER); + glShaderSource(fs, 1, &fsSource, NULL); + glCompileShader(fs); + if(isShaderError(fs)) return 0; + + free((void*)vsSource); + free((void*)fsSource); + + program = glCreateProgram(); + glAttachShader(program, vs); + glAttachShader(program, fs); + + glBindAttribLocation(program, POS_ARRAY, "vertPos"); + glBindAttribLocation(program, NORMAL_ARRAY, "vertNormal"); /* vertNormal and vertTexCoord don't need to be bound here.. ? */ + glBindAttribLocation(program, TEXCOORD_ARRAY, "vertTexCoord"); + + glLinkProgram(program); + if(isShaderError(program)) return 0; + + glUseProgram(program); + + glUniform3f(glGetUniformLocation(program, "frameLightDirection"), 1.0, 0.0, 1.0); + glUniform1i(glGetUniformLocation(program, "texture"), 0); /* 0 ?? */ + + return 1; +} + +void setUpTnL(){ + + glGenTextures(1, &texture); + glBindTexture(GL_TEXTURE_2D, texture); + + GLuint* td = malloc(sizeof(GLuint)*TEX_SIZE*TEX_SIZE); + int i,j; + for(i=0; i +#include + +/* -}{----------------------------------------------------------------------- */ + +extern void ensure_self_sub(ni_event* evq); + +/* -}{----------------------------------------------------------------------- */ + +#define TMPBUFSIZE 4096 +static char tmpbuf[TMPBUFSIZE]; + +/* -}{----------------------------------------------------------------------- */ + +static void cache_read(char*, char*, char*, int, k_stat, void*); +static void entity_written(char*, char*, char*, int, k_stat, void*); +static void write_headers(char* path, ni_resource* res); +static void headers_written(char*, char*, char*, int, k_stat, void*); + + +/* -}{----------------------------------------------------------------------- */ + +char* make_cache_path(char* uri) +{ + char* path; + size_t l=strlen(uri); + if(l>100){ k_log_out("URI too long to save: %s", uri); return 0; } + if(*(uri+l-1)!='/'){ + path=k_strdup(uri); + } + else{ + path=k_malloc(l+1+10); + sprintf(path, "%sindex.html", uri); + } + return path; +} + +void look_in_file_cache(ni_event* evq) +{ + k_hashtable* sub=evq->ent_head; + char* uri=k_hashtable_get(sub, "Sub-To:"); + size_t l=strlen(uri); + + if(l>100){ ensure_self_sub(evq); return; } + + char hdr[128]; + if(*(uri+l-1)!='/') sprintf(hdr, "%s.hdr", uri); + else sprintf(hdr, "%sindex.html.hdr", uri); + + if(strstr(hdr, "..")){ ensure_self_sub(evq); return; } + if(strchr(hdr, '?' )){ ensure_self_sub(evq); return; } + + char* c=strchr(hdr, ':'); + char* e=strchr(hdr, '/'); + if(c && c< e) *c='-'; + + k_file_read(".", k_strdup(hdr), USE_MALL, 0, cache_read, evq); +} + +void save_in_file_cache(ni_resource* res) +{ + char* uri =res->uri; + char* path =make_cache_path(uri); if(!path) return; + k_hashtable* ent_head=res->ent_head; + void* data =res->entity; + int partial =k_hashtable_is( ent_head, "Status:", "206"); + size_t size =k_hashtable_get_int(ent_head, "Content-Length:"); + char* conrange=k_hashtable_get( ent_head, "Content-Range:"); + int constant=k_hashtable_is( ent_head, "CUX:", "C"); + + if(!data){ + write_headers(path, res); + } + else + if(constant){ + int ok=k_file_sync(data, size); + if(!ok){ + k_log_err("Failed to write (sync mmap): %s", uri); + k_free(path); + return; + } + write_headers(path, res); + } + else{ + if(partial){ + k_log_out("save_in_file_cache partial %s", conrange); + if(!conrange || strncmp(conrange, "0-", 2)){ + k_log_err("not saving partial"); + k_free(path); + return; + } + size=atoi(conrange+2); + } + k_log_out("save_in_file_cache updateable: %d bytes", size); + k_file_write(".", path, data, size, entity_written, res); + } +} + +/* -}{----------------------------------------------------------------------- */ + +void entity_written(char* basedir, + char* path, + char* data, + int usedmmap, + k_stat kstat, + void* context) +{ + if(data) k_log_out("File cache entity written: %s", path); + else{ + k_log_err("Failed to write entity: %s", path); + k_free(path); + return; + } + ni_resource* res=context; + write_headers(path, res); +} + +void write_headers(char* path, ni_resource* res) +{ + snprintf(tmpbuf, TMPBUFSIZE, "%s.hdr", path); + k_free(path); path=k_strdup(tmpbuf); + k_hashtable_snprintf(res->ent_head, tmpbuf, TMPBUFSIZE); + k_file_write(".", path, tmpbuf, strlen(tmpbuf), headers_written, 0); +} + +void headers_written(char* basedir, + char* path, + char* data, + int usedmmap, + k_stat kstat, + void* context) +{ + if(data) k_log_out("File cache headers written: %s", path); + else k_log_err("Failed to write headers: %s", path); + k_free(path); +} + +void cache_read(char* basedir, + char* path, + char* data, + int usedmmap, + k_stat kstat, + void* context) +{ + if(!data) k_log_out("no cache of %s", path); + else k_log_out("file cache read: %d %s", kstat.size, path); + char* e=strstr(path, ".hdr"); + if(e){ + ni_event* evq=context; + if(!data){ k_free(path); ensure_self_sub(evq); return; } + + char* header=data; + *(header+kstat.size-2)=0; + k_hashtable* ent_head=k_hashtable_new("nHeaders/cache_read", 1); + k_hashtable_put(ent_head, "", header); + if(!ni_get_headers(header, 0, ent_head)){ + k_hashtable_delete(ent_head); + k_free(path); + ensure_self_sub(evq); + return; + } + ni_event_delete(evq); + *e=0; + ni_event* evt=ni_event_new(0, 0, ent_head, 0); + int constant=k_hashtable_is(ent_head, "CUX:", "C"); + size_t m=constant? USE_MMAP: USE_MALL; + k_file_read(".", path, m, 0, cache_read, evt); + return; + } + ni_event* evt=context; + k_free(path); + evt->entity=data; + ni_event_show(evt, "File Cache Found:"); + k_event_post("ni", evt); +} + +/* -}{----------------------------------------------------------------------- */ + + diff --git a/src/drivers/np/np.c b/src/drivers/np/np.c new file mode 100644 index 0000000..e72616b --- /dev/null +++ b/src/drivers/np/np.c @@ -0,0 +1,562 @@ + +/* -}{----------------------------------------------------------------------- */ + +#include +#include + +/* -}{----------------------------------------------------------------------- */ + +#define TMPBUFSIZE 4096 +static char tmpbuf[TMPBUFSIZE]; +static k_hashtable* own_resources; + +/* -}{----------------------------------------------------------------------- */ + +extern void run_tests(void); + +extern char* make_cache_path(char* uri); +extern void look_in_file_cache(ni_event* evq); +extern void save_in_file_cache(ni_resource* res); + +extern void init_uri2chan(void); +extern char* get_host_for(char* uri); +extern char* get_channel_for(char* host); +extern char* use_ping_info(k_hashtable*, k_channel*); +extern void use_from_info(k_hashtable*, k_channel*); +extern void ping_tunnels(void); +extern void send_ping(k_channel* chan, char* firstline, char* to); + +/* -}{----------------------------------------------------------------------- */ + +static int handles_resource(char* name); +static void sync_resource(ni_resource* res); + int connection_writable(k_channel* chan, int bufpos, int len); + int connection_readable(k_channel* chan, int bufpos, int len); +static int recv_next_event( k_channel* chan); +static void recv_request( k_channel* chan, char* header); +static void recv_response( k_channel* chan, char* header); +static void got_mmap(char*, char*, char*, int, k_stat, void*); +static void set_read_buffer(k_channel*, char*, size_t, ni_event*); +static int recv_entity( k_channel* chan, int bufpos, int eof); +static int expecting_response(char* pub, ni_event* evt, k_channel*); +static void do_request( ni_event* evq); + void ensure_self_sub(ni_event* evq); +static void ping_resource_subs(void* arg, char* key, void* val); +static void ping_sub(ni_resource* res, k_hashtable* sub); +static ni_resource* own_resource(char* uri); +static void send_request(ni_event* evq); +static void send_response(ni_event* evt); +static k_channel* ensure_chan(char* chanm); + +/* -}{----------------------------------------------------------------------- */ + +EXPORT int np_module_loaded(void) +{ + ni_register_driver("np", handles_resource, sync_resource); + + if(strstr(k_version, "test")){ + //run_tests(); + } + + init_uri2chan(); + + own_resources=k_hashtable_new("Own Resources", 0); + + k_log_out("NP Driver initialised"); + + return 0; +} + +EXPORT void np_module_tick(void) +{ + static long tix; + tix++; + if(!(tix % 1000)){ + k_hashtable_apply(own_resources, ping_resource_subs, 0); + } + if(!(tix % 3000)){ + ping_tunnels(); + } +} + +EXPORT int np_module_event(void* data) +{ + ni_event* evt=data; + if(!k_hashtable_get(evt->ent_head, "Status:")){ + do_request(evt); + } + else{ + send_response(evt); + } + return 0; +} + +/* -}{----------------------------------------------------------------------- */ + +int handles_resource(char* name) +{ + return 0; +} + +void sync_resource(ni_resource* res) +{ + save_in_file_cache(res); +} + +/* -}{----------------------------------------------------------------------- */ + +int connection_readable(k_channel* chan, int bufpos, int len) +{ + if(0) k_log_out("connection_readable %s %p %d %d %p", + chan->name, chan, bufpos, len, chan->context); + int sof=(len== 0); + int eof=(len== -1); + + if(sof) return 0; + + do{ + ni_event* evt=chan->context; + if(!evt){ + int n=recv_next_event(chan); + if(n<0) break; + bufpos-=n; + } + else{ + int n=recv_entity(chan, bufpos, eof); + if(n<0) break; + bufpos-=n; + } + + } while(1); + + if(eof && chan->context){ + ni_event* evt=chan->context; + evt->entity=0; + ni_event_delete(evt); + chan->context=0; + } + + return 0; +} + +int connection_writable(k_channel* chan, int bufpos, int len) +{ + if(0) k_log_out("connection_writable %p %d %d %p", + chan, bufpos, len, chan->context); +//if(len>20000) exit(1); + int sof=(len== 0); + int eof=(len== -1); + + if(sof){ + send_ping(chan, "PING ni/0.5" CRLF, 0); + return 0; + } + + if(eof && chan->context){ + ni_event* evt=chan->context; + evt->entity=0; + ni_event_delete(evt); + chan->context=0; + } + + return 0; +} + +/* -}{---- Receiving -------------------------------------------------------- */ + +int recv_next_event(k_channel* chan) +{ + char* header=k_channel_chop_div(chan, CRLF CRLF); + if(!header) return -1; + int n=strlen(header)+strlen(CRLF CRLF); + + if(!strncmp(header, "GET", 3) || + !strncmp(header, "SUB", 3) || + !strncmp(header, "UNSUB",5) || + !strncmp(header, "HEAD", 4) || + !strncmp(header, "PING", 4) ){ + + recv_request(chan, header); + return n; + } + if(!strncmp(header, "ni/", 4) ){ + + recv_response(chan, header); + return n; + } + k_free(header); + k_log_err("Failed reading request or response - closing connection"); + k_channel_close(chan); + return n; +} + +void recv_request(k_channel* chan, char* header) +{ + ni_event* evq; + evq=ni_get_request_headers(header); + if(!evq){ + k_log_err("Failed reading request headers - closing connection"); + k_channel_close(chan); + return; + } + if(!k_hashtable_isn(evq->evt_head, "Protocol:", "ni/", 4)){ + ni_event_delete(evq); + k_log_err("Failed reading request not ni - closing connection"); + k_channel_close(chan); + return; + } + + k_hashtable* ent_head=evq->ent_head; + int ping=k_hashtable_is(ent_head, "Method:", "PING"); + + if(!evq->uri && !ping){ + evq->uri=k_strdup(chan->name); + k_hashtable_put_dup(ent_head, "URI:", chan->name); + } + + ni_event_show(evq, "ni Protocol Request"); + + if(k_hashtable_isi(evq->evt_head, "Connection:", "Keep-Alive")){ + chan->linger=1; + if(k_hashtable_isn(ent_head, "Sub-To:", "./test", 6)){ + chan->linger=0; + } + } + if(ping){ + char* from=use_ping_info(ent_head, chan); + if(from) send_ping(chan, "ni/0.5 270 PING" CRLF, from); + ni_event_delete(evq); + return; + } + //use_from_info(ent_head, chan); + + ni_event* evp=ni_event_new(evq->uri, 0, k_hashtable_dup(ent_head), 0); + + ni_event_delete(evq); + + k_event_post("ni", evp); +} + +void recv_response(k_channel* chan, char* header) +{ + ni_event* evt=ni_get_response_headers(header); + if(!evt){ + k_log_err("recv_response: headers failed but doing nothing!"); + return; + } + char* pub= evt->uri; + k_hashtable* ent_head=evt->ent_head; + + if(!expecting_response(pub, evt, chan)) return; + + ni_event_show(evt, "Response"); + + int head=k_hashtable_is( ent_head, "Status:", "260"); + int nmod=k_hashtable_is( ent_head, "Status:", "304"); + int ping=k_hashtable_is( ent_head, "Status:", "270"); + int cl =k_hashtable_get_int(ent_head, "Content-Length:"); + int entity=!(head || nmod || ping || cl==0); + + if(ping){ + use_ping_info(ent_head, chan); + ni_event_delete(evt); + return; + } + use_from_info(ent_head, chan); + + if(entity){ + k_hashtable_set(ent_head, "Status:", "260"); + k_hashtable_set(ent_head, "Status-Text:", "Headers Only"); + } + k_event_post("ni", evt); + + if(entity){ + + k_hashtable* eh=k_hashtable_new("nHeaders/recv_response", 1); + char* from =k_hashtable_get(ent_head, "From:"); + char* contlen =k_hashtable_get(ent_head, "Content-Length:"); + char* cux =k_hashtable_get(ent_head, "CUX:"); + k_hashtable_set(eh, "Status:", "206"); + k_hashtable_set(eh, "Status-Text:", "Partial Content"); + k_hashtable_put_dup(eh, "From:", from); + k_hashtable_put_dup(eh, "Content-Length:", contlen); + k_hashtable_put_dup(eh, "CUX:", cux); + ni_event* evc=ni_event_new(pub, 0, eh, 0); + chan->context=evc; + + int constant=k_hashtable_is(ent_head, "CUX:", "C"); + if(constant){ + char* path=make_cache_path(pub); if(!path) return; + k_file_read(".", path, USE_MMAP, cl, got_mmap, chan); + } + else{ + char* data=k_malloc(cl); + set_read_buffer(chan, data, cl, evc); + } + } +} + +void got_mmap(char* basedir, + char* path, + char* data, + int usedmmap, + k_stat kstat, + void* context){ + + k_free(path); + k_channel* chan=context; + ni_event* evt=chan->context; + if(!evt){ k_log_err("got_mmap: evt=0"); return; } + if(!data || !usedmmap){ k_log_err("got_mmap: mmap failed"); return; } + + size_t cl=k_hashtable_get_int(evt->ent_head, "Content-Length:"); + set_read_buffer(chan, data, cl, evt); +} + +void set_read_buffer(k_channel* chan, char* data, size_t cl, ni_event* evt) +{ + evt->entity=data; + int r=k_channel_setbuf(chan, data, cl); + if(0) k_log_out("k_channel_setbuf %d", r); + if(r==BUFFER_ALREADY_SET){ + k_log_err("oops! k_channel_setbuf BUFFER_ALREADY_SET"); + return; + } + if(r==BUFFER_FILLED){ + k_hashtable_set(evt->ent_head, "Status:", "200"); + k_hashtable_set(evt->ent_head, "Status-Text:", "OK"); + chan->context=0; + k_event_post("ni", evt); + } +} + +int recv_entity(k_channel* chan, int bufpos, int eof) +{ + ni_event* evt=chan->context; + k_hashtable* ent_head=evt->ent_head; + + char* cls=k_hashtable_get( ent_head, "Content-Length:"); + int cl =k_hashtable_get_int(ent_head, "Content-Length:"); + + if(!cls && !eof) return -1; + + int partial=0; + int eofcontlen=eof && (!cls || bufpos < cl); + if(eofcontlen){ + if(cls){ + char* clg=k_strdup(cls); + k_hashtable_put(ent_head, "Content-Length-Given:", clg); + partial=1; + } + cl=bufpos; + char b[32]; snprintf(b, 32, "%d", cl); + k_hashtable_put_dup(ent_head, "Content-Length:", b); + } + + if(bufpos < cl){ + if(bufpos){ + ni_event* evp=ni_event_dup(evt); + snprintf(tmpbuf, TMPBUFSIZE, "0-%d", bufpos); + char* cr=k_strdup(tmpbuf); + k_hashtable_put(evp->ent_head, "Content-Range:", cr); + k_event_post("ni", evp); + } + return -1; + } + + static char dummy_empty_entity[0]; + if(!k_channel_getbuf(chan)){ + int cn=k_hashtable_is(ent_head, "CUX:", "C"); + if(cl) evt->entity=k_channel_chop_len(chan, cl); + else evt->entity=cn? dummy_empty_entity: k_malloc(1); + } + + if(!partial){ + k_hashtable_set(ent_head, "Status:", "200"); + k_hashtable_set(ent_head, "Status-Text:", "OK"); + } + chan->context=0; + k_event_post("ni", evt); + + return cl; +} + +int expecting_response(char* pub, ni_event* evt, k_channel* chan) +{ + if(pub && 0){ + k_log_err("unwanted response: %s", pub); + ni_event_delete(evt); + k_channel_close(chan); + return 0; + } + return 1; +} + +/* -}{---- Sending ---------------------------------------------------------- */ + +void do_request(ni_event* evq) +{ + k_hashtable* sub=evq->ent_head; + int tc=k_hashtable_isi(sub, "Sub-Type:", "Cache"); + int to=k_hashtable_isi(sub, "Sub-Type:", "Original"); + + if(tc){ + char* ims=k_hashtable_get(sub, "If-Modified-Since:"); + if(ims) ensure_self_sub(evq); + else look_in_file_cache(evq); + } + else + if(to){ + send_request(evq); + } +} + +void ensure_self_sub(ni_event* evq) +{ + k_hashtable* sub=evq->ent_head; + char* pub=k_hashtable_get(sub, "Sub-To:"); + + ni_resource* res=own_resource(pub); + k_hashtable* enh=res->ent_head; + k_hashtable* selfsub=k_hashtable_get(enh, "Sub-To:"); + if(selfsub && !k_hashtable_is(selfsub, "Status-Cache:", "OK")){ + k_log_err("cancel selfsub as new one needed"); + } + + k_hashtable* ss=k_hashtable_dup(sub); + k_hashtable_remove( ss, "From:"); + k_hashtable_put_dup(ss, "URI:", pub); + k_hashtable_set( ss, "Sub-Type:", "Original"); + k_hashtable_put_dup(ss, "Via:", get_host_for(pub)); + if(k_hashtable_get( ss, "If-Modified-Since:")){ + char* lm=k_hashtable_get(enh, "Last-Modified:"); + if(!res->entity) lm=0; + k_hashtable_set(ss, "If-Modified-Since:", lm? lm: "0"); + } + ni_event* evs=ni_event_new(0, 0, ss, 0); + k_event_post("ni", evs); + + ni_event_delete(evq); +} + +void ping_resource_subs(void* arg, char* key, void* val) +{ + ni_resource* res=val; + k_hashtable* pubcache=k_hashtable_get(res->ent_head, "Pub-Cache:"); + if(!pubcache || !k_hashtable_get(pubcache, "Method:")) return; + k_hashtable* subs=k_hashtable_get(res->ent_head, "Sub-To:"); + k_hashtable* sub; + for(sub=subs; sub; sub=sub->next){ + if(!k_hashtable_is(sub, "Status-Cache:", "OK")){ + if(!k_hashtable_get(sub, "Status:")){ + ping_sub(res, sub); + } + else{ + int ts=k_hashtable_get_int(sub, "Timestamp:"); + if(0) k_log_out("check dried-up request: %d", ts); + } + } + } +} + +void ping_sub(ni_resource* res, k_hashtable* sub) +{ + ni_resource_show(res, "ping_resource_subs"); + + k_hashtable* ss=k_hashtable_dup(sub); + + char* subto=k_hashtable_extract(ss, "URI:"); + k_hashtable_put_dup(ss, "URI:", res->uri); + k_hashtable_put( ss, "Sub-To:", subto); + k_hashtable_set( ss, "Sub-Type:", "Original"); + k_hashtable_put_dup(ss, "Via:", get_host_for(res->uri)); + + ni_event* evs=ni_event_new(0, 0, ss, 0); + k_event_post("ni", evs); +} + +ni_resource* own_resource(char* uri) +{ + ni_resource* res=k_hashtable_get(own_resources, uri); + if(!res){ + res=ni_resource_get(uri); + k_hashtable_set(own_resources, uri, res); + } + return res; +} + +void send_request(ni_event* evt) +{ + k_hashtable* eh=evt->ent_head; + char* method=k_strdup(k_hashtable_get(eh, "Method:")); + char* to =k_strdup(k_hashtable_get(eh, "Sub-To:")); + char* via =k_strdup(k_hashtable_get(eh, "Via:")); + + char* chanm=get_channel_for(via); + if(!chanm) goto free_and_return; + + k_channel* chan=ensure_chan(chanm); + if(!chan) goto free_and_return; + + ni_fix_ni_headers(eh, 0); + ni_request(evt, to, method, chan); + + free_and_return: + k_free(method); k_free(to); k_free(via); + ni_event_delete(evt); +} + +void send_response(ni_event* evt) +{ + ni_event_show(evt, "send_response"); + + k_hashtable* eh=evt->ent_head; + + k_hashtable* sub=k_hashtable_get(eh, "Pub-To:"); + char* uri =k_hashtable_get(sub, "URI:"); + char* from =k_hashtable_get(sub, "From:"); + char* method =k_hashtable_get(sub, "Method:"); + int methead =k_hashtable_is( sub, "Method:", "HEAD"); + + char* to=from? from: uri; + + char* host=from? from: get_host_for(uri); + char* chanm=get_channel_for(host); + if(!chanm){ + if(0) k_log_out("no ni protocol channel %s", to); + ni_event_delete(evt); + return; + } + + k_channel* chan=ensure_chan(chanm); + if(!chan){ + if(0) k_log_out("no ni protocol channel %s", to); + ni_event_delete(evt); + return; + } + + k_hashtable_extract(eh, "Pub-To:"); + + char* protocol="ni/0.5"; + + ni_fix_ni_headers(eh, methead); + ni_response(evt, to, method, protocol, 0, chan); + + k_hashtable_delete(sub); + evt->entity=0; + ni_event_delete(evt); +} + +k_channel* ensure_chan(char* chanm) +{ + k_channel* chan=k_channel_get_name(chanm); + if(!chan){ + k_log_err("Cannot find current channel for %s", chanm); + k_channel_connect_name(chanm, connection_readable, + connection_writable); + } + return chan; +} + +/* -}{----------------------------------------------------------------------- */ + diff --git a/src/drivers/np/uri2chan.c b/src/drivers/np/uri2chan.c new file mode 100644 index 0000000..b4de726 --- /dev/null +++ b/src/drivers/np/uri2chan.c @@ -0,0 +1,294 @@ + +/* -}{----------------------------------------------------------------------- */ + +#include +#include + +/* -}{----------------------------------------------------------------------- */ + +extern int connection_writable(k_channel* chan, int bufpos, int len); +extern int connection_readable(k_channel* chan, int bufpos, int len); + +/* -}{----------------------------------------------------------------------- */ + +#define TMPBUFSIZE 4096 +static char tmpbuf[TMPBUFSIZE]; +static int is_np; +static char* nexus_channel; +static k_hashtable* chans_for_host; + +/* -}{----------------------------------------------------------------------- */ + +static void nexus_file_read(char*, char*, char*, int, k_stat, void*); +static void write_nexus(char* hostname); +static void nexus_file_written(char*, char*, char*, int, k_stat, void*); +static int root_nexus(void); +static void listen_http(void); +static void listen_nexus(void); +static void connect_to_nexus(void); +static void ping_this(void* arg, char* key, void* val); +static char* generate_new_hostname(k_hashtable* ent_head, k_channel* chan); +static void set_channel_for(char* host, char* chanm); +static void show_np(void); +static void show_list(void* arg, char* key, void* val); + +/* -}{----------------------------------------------------------------------- */ + +void init_uri2chan(void) +{ + chans_for_host =k_hashtable_new("Channels for Host", 0); + + k_file_read(".", "nexus.txt", USE_MALL, 0, nexus_file_read, 0); + + is_np=!strcmp(k_ciux, "np"); + if(is_np) listen_nexus(); + if(root_nexus()){ + k_log_out("root nexus - but listening on http"); + listen_http(); + } + else{ + k_log_out("connecting to nexus %s", nexus_channel); + connect_to_nexus(); + } +} + +char* get_host_for(char* uri) +{ + if(0) k_log_out("get_host_for %s", uri); + char* host=0; + if(0) k_log_out("host for %s=%s", uri, host); + return host; +} + +char* get_channel_for(char* host) +{ + if(0) k_log_out("get_channel_for %s", host); + char* chanm=k_hashtable_get(chans_for_host, host); + if(!chanm){ + } + if(0) k_log_out("channel for %s=%s", host, chanm); + return chanm; +} + +char* use_ping_info(k_hashtable* ent_head, k_channel* chan) +{ + char* from=k_hashtable_get(ent_head, "From:"); + char* to =k_hashtable_get(ent_head, "To:"); + + if(!from) return 0; + if(!strcmp(from, "-")) from=generate_new_hostname(ent_head, chan); + if(!from) return 0; + if(!ni_hostname() && to) write_nexus(to); + + set_channel_for(from, chan->name); + + if(0) k_log_out("%s PING %s", chan->name, from); + if(0) show_np(); + + return from; +} + +void use_from_info(k_hashtable* ent_head, k_channel* chan) +{ + char* from=k_hashtable_get(ent_head, "From:"); + + if(!from) return; + + set_channel_for(from, chan->name); + + if(0) show_np(); +} + +void ping_tunnels(void) +{ + k_hashtable* channelspinged=k_hashtable_new("channelspinged", 0); + k_hashtable_apply(chans_for_host, ping_this, channelspinged); + k_hashtable_delete(channelspinged); +} + +void send_ping(k_channel* chan, char* firstline, char* to) +{ + char* from=ni_hostname(); + if(!from) from="-"; + + int ln=0; + int bufsize=TMPBUFSIZE; + + ln+=snprintf(tmpbuf+ln, bufsize-ln, firstline); + if(ln>=bufsize) return; + ln+=snprintf(tmpbuf+ln, bufsize-ln, "From: %s" CRLF, from); + if(ln>=bufsize) return; + if(to){ + ln+=snprintf(tmpbuf+ln, bufsize-ln, "To: %s" CRLF, to); + if(ln>=bufsize) return; + } + ln+=snprintf(tmpbuf+ln, bufsize-ln, "Server: %s" CRLF, k_version); + if(ln>=bufsize) return; + ln+=snprintf(tmpbuf+ln, bufsize-ln, "Channels: %s" CRLF, "-"); + if(ln>=bufsize) return; + ln+=snprintf(tmpbuf+ln, bufsize-ln, CRLF); + if(ln>=bufsize) return; + + char* head=k_strdup(tmpbuf); + k_channel_send(chan, head, ln, FREE_ON_SENT); +} + +/* -}{----------------------------------------------------------------------- */ + +void nexus_file_read(char* basedir, + char* filepath, + char* data, + int usedmmap, + k_stat kstat, + void* context) +{ + int L=0; + if(data) data[kstat.size-1]=0; + else data=k_strdup(""); + k_hashtable* nex=k_hashtable_new("nexus", 1); + if(!ni_get_headers(data, nex, 0)){ + k_log_err("Corrupt nexus.txt file"); + k_hashtable_delete(nex); + k_free(data); + data=k_strdup(""); + nex=k_hashtable_new("nexus", 1); + ni_get_headers(data, nex, 0); + } + + char* hostname; + nexus_channel=k_hashtable_get_dup(nex, "Nexus-Channel:"); + hostname =k_hashtable_get( nex, "Hostname:"); + + if(hostname) ni_hostname_set(hostname); + + if(L) if(nexus_channel) k_log_out("Nexus-Channel: %s", nexus_channel); + if(L) if(hostname) k_log_out("Hostname: %s", hostname); + + k_hashtable_delete(nex); + k_free(data); +} + +void write_nexus(char* hostname) +{ + ni_hostname_set(hostname); + + snprintf(tmpbuf, TMPBUFSIZE, "Nexus-Channel: %s\r\n" + "Hostname: %s\r\n", nexus_channel, + hostname); + + k_file_write(".", "nexus.txt", tmpbuf, strlen(tmpbuf), + nexus_file_written, 0); +} + +void nexus_file_written(char* basedir, + char* filepath, + char* data, + int usedmmap, + k_stat kstat, + void* context) +{ + if(!data) k_fatal("Failed to write nexus.txt"); + else k_log_out("New nexus.txt written:\n%s", data); +} + +int root_nexus(void) +{ + char* hostname=ni_hostname(); + if(!hostname || strchr(hostname, '-')) return 0; + return 1; +} + +void listen_http(void) +{ + char* chanm="|nip-server|-|8081|-|"; + k_channel_connect_name(chanm, connection_readable, + connection_writable); +} + +void listen_nexus(void) +{ + char* chanm="|nip-server|-|7747|-|"; + k_channel_connect_name(chanm, connection_readable, + connection_writable); +} + +void connect_to_nexus(void) +{ + if(nexus_channel){ + k_channel_connect_name(nexus_channel, connection_readable, + connection_writable); + } + else k_log_out("discovery of nexus not implemented yet!"); +} + +void ping_this(void* arg, char* key, void* val) +{ + k_hashtable* channelspinged=arg; + char* chanm=val; + + if(strncmp(chanm, "nip-client", 11) ) return; + if(k_hashtable_get(channelspinged, chanm)) return; + k_hashtable_set(channelspinged, chanm, chanm); + + k_channel* chan=k_channel_get_name(chanm); + //k_log_out("ping_this: host %s channel %s got=%p", key, chanm, chan); + + if(!chan) k_channel_connect_name(chanm, connection_readable, + connection_writable); + + int pingtodeath=0; + if(chan && pingtodeath) send_ping(chan, "PING ni/0.5" CRLF, 0); +} + +char* generate_new_hostname(k_hashtable* ent_head, k_channel* chan) +{ + if(!ni_hostname()) return 0; + unsigned long a=chan->clientip.s_addr; + #define IPQUAD(a) \ + ((unsigned char *)&a)[0], \ + ((unsigned char *)&a)[1], \ + ((unsigned char *)&a)[2], \ + ((unsigned char *)&a)[3] + snprintf(tmpbuf, TMPBUFSIZE, "%s-%02x%02x%02x%02x.%d", + ni_hostname(), IPQUAD(a), 7747); + if(0) k_log_out("generated new hostname: %s\n", tmpbuf); + return k_strdup(tmpbuf); +} + +/* -}{----------------------------------------------------------------------- */ + +void set_channel_for(char* host, char* chanm) +{ + k_hashtable_put_dup(chans_for_host, host, chanm); +} + +void show_np(void) +{ + k_log_out("-------- channel for host ---------------"); + k_hashtable_apply(chans_for_host, show_list, 0); + k_channel_show_all(); +} + +void show_list(void* arg, char* key, void* val) +{ + char* chanm=val; + k_log_out("%s %s", key, chanm); +} + +void generate_random(void) +{ + time_t t=time(0); + short r; + k_random_bytes((char*)&r, sizeof(r)); + unsigned char r1=(t >>24) & 255; + unsigned char r2=(t >>16) & 255; + unsigned char r3=(t >> 8) & 255; + unsigned char r4=(t ) & 255; + unsigned char r5=(r >> 8) & 255; + unsigned char r6=(r ) & 255; + snprintf(tmpbuf, TMPBUFSIZE, "%02x-%02x-%02x-%02x-%02x-%02x", + r1, r2, r3, r4, r5, r6); +} + +/* -}{----------------------------------------------------------------------- */ + diff --git a/src/include/kernelapi.h b/src/include/kernelapi.h new file mode 100644 index 0000000..c659303 --- /dev/null +++ b/src/include/kernelapi.h @@ -0,0 +1,218 @@ +#ifndef KERNEL_H +#define KERNEL_H + +/* + This API is the equivalent of a system call API and thereby + insulates its user from a GPL implementation. +*/ + +#include + +/* -------------------------------------------------------------------------- */ + +PUBLIC CONSOLE k_console; +PUBLIC char* k_version; +PUBLIC char* k_ciux; +PUBLIC void (*k_terminate)(void); + +/* -------------------------------------------------------------------------- */ + +#define NAME_OF_MODULE_LOADED_FN "_module_loaded" + +typedef int (*k_module_loaded_fn)(void); + +/* -------------------------------------------------------------------------- */ + +#define NAME_OF_MODULE_TICK_FN "_module_tick" + +typedef void (*k_module_tick_fn)(void); + +/* -------------------------------------------------------------------------- */ + +#define NAME_OF_MODULE_EVENT_FN "_module_event" + +typedef int (*k_module_event_fn)(void* data); + +PUBLIC int k_event_post(char* module, void* data); + +/* -------------------------------------------------------------------------- */ + +typedef void (*k_gl_reshape_event)(int width, int height); +typedef void (*k_gl_draw_event)(void); +typedef void (*k_gl_key_event)(unsigned char k, int down); +PUBLIC void k_gl_register_reshape(k_gl_reshape_event); +PUBLIC void k_gl_register_draw( k_gl_draw_event); +PUBLIC void k_gl_register_key( k_gl_key_event); +PUBLIC void gluxSwapBuffers(void); + +/* -------------------------------------------------------------------------- */ + +#define CRLF "\015\012" +#define CHAN_LISTEN 1 +#define CHAN_ESTABL 2 +#define CHAN_ANY 3 + +typedef struct k_channel k_channel; +typedef struct k_channel_priv k_channel_priv; + +typedef int (*k_channel_event)(k_channel* chan, + int bufpos, + int size); + +struct k_channel{ struct k_channel* next; + + char* name; + char* listenhost; + int listenport; + char* banner; + k_channel_event rdcallback; + k_channel_event wrcallback; + void* context; + int type; + struct in_addr clientip; + int linger; + k_channel_priv* priv; +}; + +PUBLIC char* k_channel_name(char* type, char* ip, int port, char* other); +PUBLIC k_channel* k_channel_connect_name(char* chanm, + k_channel_event rdcallback, + k_channel_event wrcallback); +PUBLIC k_channel* k_channel_get_name(char* chanm); +PUBLIC void k_channel_show_all(void); + +PUBLIC k_channel* k_channel_listen(char* name, + int listenport, + char* banner, + k_channel_event rdcallback, + k_channel_event wrcallback, + void* context); + +PUBLIC k_channel* k_channel_connect(char* name, + char* listenhost, + int listenport, + k_channel_event rdcallback, + k_channel_event wrcallback, + void* context); + +#define FREE_ON_SENT 1 +PUBLIC k_channel* k_channel_send(k_channel* chan, + char* base, + size_t size, + int free); + +#define BUFFER_ALREADY_SET -1 +#define BUFFER_FILLED 1 +#define BUFFER_SET 0 +PUBLIC char* k_channel_chop_div( k_channel* chan, char* divider); +PUBLIC char* k_channel_chop_len( k_channel* chan, size_t size); +PUBLIC int k_channel_setbuf( k_channel* chan, char* rdbuffer, size_t size); +PUBLIC char* k_channel_getbuf( k_channel* chan); + +PUBLIC void k_channel_close(k_channel* chan); + +/* -------------------------------------------------------------------------- */ + +#define STAT_F 0100000 +#define STAT_D 0040000 +#define STAT_L 0120000 +#define MAX_FILESIZE (1024*1024*1024) +#define USE_MMAP 0 +#define USE_MALL MAX_FILESIZE + +typedef struct k_stat{ + int type; + size_t size; + time_t time; + mode_t perm; +} k_stat; + +typedef void (*k_file_event)(char* basedir, + char* filename, + char* data, + int usedmmap, + k_stat stat, + void* context); + +PUBLIC void k_file_stat( char* basedir, char* filename, + k_file_event callback, void* context); +PUBLIC void k_file_read( char* basedir, char* filename, size_t msz, size_t size, + k_file_event callback, void* context); +PUBLIC void k_file_write(char* basedir, char* filename, char* data, size_t size, + k_file_event callback, void* context); +PUBLIC void k_dir_list( char* basedir, char* dirname, + char* pattern[], char* format[], + k_file_event callback, void* context); +PUBLIC int k_file_sync(char* data, size_t size); + +/* -------------------------------------------------------------------------- */ + +typedef struct k_hashtable{ struct k_hashtable* next; } k_hashtable; + +typedef void (*k_hashtable_apply_fn)(void* arg, char* key, void* val); + +PUBLIC k_hashtable* k_hashtable_new(char* name, int ignorecase); +PUBLIC k_hashtable* k_hashtable_dup( k_hashtable* tab); +PUBLIC k_hashtable* k_hashtable_deep(k_hashtable* tab); +PUBLIC k_hashtable* k_hashtable_dip( k_hashtable* tab, char* includes[]); +PUBLIC void k_hashtable_merge( k_hashtable* tab, k_hashtable* tam); +PUBLIC void k_hashtable_set( k_hashtable* tab, char* key, void* val); +PUBLIC void k_hashtable_put( k_hashtable* tab, char* key, void* val); +PUBLIC void k_hashtable_put_dup(k_hashtable* tab, char* key, char* val); +PUBLIC void k_hashtable_put_int(k_hashtable* tab, char* key, int val); +PUBLIC void k_hashtable_sub( k_hashtable* tab, char* key, k_hashtable* sub); +PUBLIC void* k_hashtable_get( k_hashtable* tab, char* key); +PUBLIC char* k_hashtable_get_dup(k_hashtable* tab, char* key); +PUBLIC int k_hashtable_get_int(k_hashtable* tab, char* key); +PUBLIC int k_hashtable_is( k_hashtable* tab, char* key, char* val); +PUBLIC int k_hashtable_isi( k_hashtable* tab, char* key, char* val); +PUBLIC int k_hashtable_isn( k_hashtable* tab, char* key, char* val,size_t); +PUBLIC void k_hashtable_remove( k_hashtable* tab, char* key); +PUBLIC void* k_hashtable_extract(k_hashtable* tab, char* key); +PUBLIC void k_hashtable_apply( k_hashtable* tab, k_hashtable_apply_fn, void*); +PUBLIC void k_hashtable_show( k_hashtable* tab); +PUBLIC void k_hashtable_show_chars(k_hashtable* tab); +PUBLIC int k_hashtable_snprintf( k_hashtable* tab, char* buf, size_t size); +PUBLIC int k_hashtable_snprintf_i(k_hashtable* tab, char* buf, size_t size, + char* includes[]); +PUBLIC int k_hashtable_snprintf_x(k_hashtable* tab, char* buf, size_t size, + char* excludes[]); +PUBLIC void k_hashtable_delete(k_hashtable* tab); + +/* -------------------------------------------------------------------------- */ + +PUBLIC int k_string_matches_pattern(char* string, char* pattern); +PUBLIC int k_string_ends_with( char* string, char* postfix); +PUBLIC void k_string_url_decode( char* string); + +/* -------------------------------------------------------------------------- */ + +PUBLIC char* k_time_to_rfc(time_t t); +PUBLIC char* k_time_to_rfc_relative(int plus); +PUBLIC time_t k_time_from_rfc(char* s); +PUBLIC void k_time_get_now(char* buf, size_t size, char* format); + +/* -------------------------------------------------------------------------- */ + +PUBLIC void k_random_bytes(char* buf, size_t size); + +/* -------------------------------------------------------------------------- */ + +PUBLIC void* k_malloc(size_t size); +PUBLIC void* k_realloc(void* o, size_t size); +PUBLIC char* k_strdup(char* s); +PUBLIC void* k_memdup(void* s, size_t size); +PUBLIC void k_free(void* o); + +/* -------------------------------------------------------------------------- */ + +PUBLIC void k_log_out(char* format, ...); +PUBLIC void k_log_err(char* format, ...); +PUBLIC void k_fatal( char* format, ...); + +/* -------------------------------------------------------------------------- */ + + +#endif + + diff --git a/src/include/ni.h b/src/include/ni.h new file mode 100644 index 0000000..32c90b2 --- /dev/null +++ b/src/include/ni.h @@ -0,0 +1,77 @@ + +#ifndef ni_H +#define ni_H + +/* -------------------------------------------------------------------------- */ + +PUBLIC char* ni_hostname(void); +PUBLIC void ni_hostname_set(char* name); + +/* -------------------------------------------------------------------------- */ + +typedef struct ni_resource{ + char* uri; + k_hashtable* ent_head; + void* entity; +} ni_resource; + +PUBLIC ni_resource* ni_resource_new(char* uri, + k_hashtable* ent_head, + char* entity); +PUBLIC ni_resource* ni_resource_dup( ni_resource* res); +PUBLIC void ni_resource_delete(ni_resource* res); +PUBLIC void ni_resource_show( ni_resource* res, char* text); +PUBLIC ni_resource* ni_resource_get(char* uri); + +/* -------------------------------------------------------------------------- */ + +typedef struct ni_event{ + char* uri; + k_hashtable* evt_head; + k_hashtable* ent_head; + void* entity; +} ni_event; + +PUBLIC ni_event* ni_event_new(char* uri, + k_hashtable* evt_head, + k_hashtable* ent_head, + char* entity); +PUBLIC ni_event* ni_event_dup( ni_event* res); +PUBLIC void ni_event_delete(ni_event* res); +PUBLIC void ni_event_show( ni_event* res, char* text); + +/* -------------------------------------------------------------------------- */ + +PUBLIC ni_event* ni_res_to_evt(ni_resource* res); + +/* -------------------------------------------------------------------------- */ + +typedef int (*ni_handles_resource)(char*); +typedef void (*ni_sync_resource )(ni_resource*); + +PUBLIC void ni_register_driver(char* name, + ni_handles_resource handles_resource, + ni_sync_resource sync_resource); + +/* -------------------------------------------------------------------------- */ + +PUBLIC ni_event* ni_get_request_headers( char* header); +PUBLIC ni_event* ni_get_response_headers(char* header); +PUBLIC char* ni_get_headers( char* header, k_hashtable*, k_hashtable*); +PUBLIC void ni_fix_http_headers(k_hashtable* ent_head); +PUBLIC void ni_fix_ni_headers(k_hashtable* ent_head, int methead); +PUBLIC void ni_response(ni_event* evt, + char* to, + char* method, + char* protocol, + char* connection, + k_channel* chan); +EXPORT void ni_request(ni_event* evt, + char* to, + char* method, + k_channel* chan); + +/* -------------------------------------------------------------------------- */ + +#endif + diff --git a/src/ni/headers.c b/src/ni/headers.c new file mode 100644 index 0000000..391fcc2 --- /dev/null +++ b/src/ni/headers.c @@ -0,0 +1,523 @@ + +/* -}{----------------------------------------------------------------------- */ + +#include +#undef PUBLIC +#define PUBLIC EXPORT +#include + +/* -}{---- ------------------------------------------------------------------ */ + +static k_hashtable* entity_headers; + +/* -}{---- ------------------------------------------------------------------ */ + +#define TMPBUFSIZE 4096 +static char tmpbuf[TMPBUFSIZE]; + +/* -}{----------------------------------------------------------------------- */ + +static char* get_request( char* header, k_hashtable*, k_hashtable*); +static char* get_response(char* header, k_hashtable*, k_hashtable*); +static char* get_val(char** atp); +static char* end_of_val(char* at); +static void fix_keepalive(k_hashtable* evt_head); +static void fix_cache_control(k_hashtable* ent_head); +static void fix_uri(k_hashtable* ent_head); +static void fix_subscribe(k_hashtable*, k_hashtable*); + +/* -}{----------------------------------------------------------------------- */ + +#define WHITESPACEH " \t" +#define WHITESPACEV "\012\015" +#define WHITESPACE WHITESPACEH WHITESPACEV + +EXPORT ni_event* ni_get_request_headers(char* header) +{ + k_hashtable* evt_head=k_hashtable_new("vHeaders/get_req_hdrs", 1); + k_hashtable* ent_head=k_hashtable_new("nHeaders/get_req_hdrs", 1); + k_hashtable_put(ent_head, "", header); + + char* h=get_request(header, evt_head, ent_head); + if(!h){ + k_hashtable_delete(evt_head); + k_hashtable_delete(ent_head); + return 0; + } + + h=ni_get_headers(h, evt_head, ent_head); + if(!h){ + k_hashtable_delete(evt_head); + k_hashtable_delete(ent_head); + return 0; + } + + fix_keepalive(evt_head); + fix_cache_control(ent_head); + fix_uri(ent_head); + fix_subscribe(evt_head, ent_head); + + ni_event* evq=ni_event_new(0, evt_head, ent_head, 0); + + return evq; +} + +EXPORT ni_event* ni_get_response_headers(char* header) +{ + k_hashtable* evt_head=k_hashtable_new("vHeaders/get_resp_hdrs", 1); + k_hashtable* ent_head=k_hashtable_new("nHeaders/get_resp_hdrs", 1); + k_hashtable_put(ent_head, "", header); + + char* h=get_response(header, evt_head, ent_head); + if(!h){ + k_hashtable_delete(evt_head); + k_hashtable_delete(ent_head); + return 0; + } + + h=ni_get_headers(h, evt_head, ent_head); + if(!h){ + k_hashtable_delete(evt_head); + k_hashtable_delete(ent_head); + return 0; + } + + ni_event* evt=ni_event_new(0, evt_head, ent_head, 0); + + return evt; +} + +EXPORT char* ni_get_headers(char* header, + k_hashtable* evt_head, + k_hashtable* ent_head) +{ + char* at=header; + char* tag; + char* val; + while(*at){ + + tag=at; + at=strpbrk(at, WHITESPACE); + if(!at || at==tag) return 0; + char* e=at; + val=get_val(&at); + *e=0; + + if(strlen(val) > 2048) return 0; + + k_hashtable* h; + h=k_hashtable_get(entity_headers, tag)? ent_head: evt_head; + if(h){ + char* old=k_hashtable_get(h, tag); + if(!old) k_hashtable_set(h, tag, val); + } + } + return at; +} + +EXPORT void ni_fix_http_headers(k_hashtable* ent_head) +{ + if(k_hashtable_is(ent_head, "Status:", "260")){ + char* crn=k_hashtable_get(ent_head, "Content-Range:"); + char* clg=k_hashtable_get(ent_head, "Content-Length-Given:"); + if(crn || clg){ + k_hashtable_set(ent_head, "Status:", "206"); + k_hashtable_set(ent_head, "Status-Text:", "Partial Content"); + } + else{ + k_hashtable_set(ent_head, "Status:", "200"); + k_hashtable_set(ent_head, "Status-Text:", "OK"); + } + } + k_hashtable_remove(ent_head, "URI:"); + k_hashtable_remove(ent_head, "Method:"); + k_hashtable_remove(ent_head, "From:"); + k_hashtable_remove(ent_head, "Content-Length-Given:"); + k_hashtable_remove(ent_head, "Last-Modified-Epoch:"); + + char* cc=k_hashtable_get(ent_head, "Cache-Control:"); + if(cc && strstr(cc, "no-cache")){ + k_hashtable_set(ent_head, "Pragma:", "no-cache"); + } +} + +EXPORT void ni_fix_ni_headers(k_hashtable* ent_head, int methead) +{ + if(methead && (k_hashtable_is(ent_head, "Status:", "200") || + k_hashtable_is(ent_head, "Status:", "206") )){ + + k_hashtable_set(ent_head, "Status:", "260"); + k_hashtable_set(ent_head, "Status-Text:", "Headers Only"); + } + k_hashtable_remove(ent_head, "Method:"); + k_hashtable_remove(ent_head, "Sub-To:"); + k_hashtable_remove(ent_head, "Via:"); + k_hashtable_set( ent_head, "From:", ni_hostname()); + + char* uri=k_hashtable_get(ent_head, "URI:"); + if(*uri=='.'){ + snprintf(tmpbuf, TMPBUFSIZE, "%s/%s", ni_hostname(), uri+2); + k_hashtable_put_dup(ent_head, "URI:", tmpbuf); + } +} + +EXPORT void ni_response(ni_event* evt, + char* to, + char* method, + char* protocol, + char* connection, + k_channel* chan) +{ + int L=0; + snprintf(tmpbuf, TMPBUFSIZE, "%s", k_time_to_rfc_relative(0)); + k_hashtable_put_dup(evt->evt_head, "Date:", tmpbuf); + k_hashtable_set( evt->evt_head, "Server:", k_version); + if(connection) + k_hashtable_put_dup(evt->evt_head, "Connection:", connection); + + int status =k_hashtable_get_int(evt->ent_head, "Status:"); + char* statustext=k_hashtable_get( evt->ent_head, "Status-Text:"); + int datalength=k_hashtable_get_int(evt->ent_head, "Content-Length:"); + int constant =k_hashtable_is( evt->ent_head, "CUX:", "C"); + + char* buf =tmpbuf; + int bufsize=TMPBUFSIZE; + int ln=0; + + ln+=snprintf(buf+ln, bufsize-ln, "%s %d %s" CRLF, + protocol, status, statustext); + if(ln>=bufsize) return; + + static char* exheaders[]={ "Status:", "Status-Text:", + "Protocol:", "CUX:", 0 }; + + ln+=k_hashtable_snprintf_x(evt->evt_head, buf+ln,bufsize-ln, exheaders); + if(ln>=bufsize) return; + + ln+=k_hashtable_snprintf_x(evt->ent_head, buf+ln,bufsize-ln, exheaders); + if(ln>=bufsize) return; + + ln+=snprintf(buf+ln, bufsize-ln, CRLF); + if(ln>=bufsize) return; + + char* head=k_strdup(buf); + if(L) k_log_out("Actual response headers:\n%s", head); + k_channel_send(chan, head, ln, FREE_ON_SENT); + + if(evt->entity && datalength){ + k_channel_send(chan, evt->entity, datalength, !constant); + } + else{ + datalength=0; + if(!constant) k_free(evt->entity); + } + k_log_out("%s %s %s %d %d", to, method, evt->uri, status, datalength); +} + +EXPORT void ni_request(ni_event* evt, char* to, char* method, k_channel* chan) +{ + k_hashtable* sub=evt->ent_head; + + int ln=0; + int bufsize=TMPBUFSIZE; + + ln+=snprintf(tmpbuf+ln, bufsize-ln, "%s //%s ni/0.5" CRLF, method, to); + if(ln>=bufsize) return; + + ln+=k_hashtable_snprintf(sub, tmpbuf+ln, bufsize-ln); + if(ln>=bufsize) return; + + ln+=snprintf(tmpbuf+ln, bufsize-ln, CRLF); + if(ln>=bufsize) return; + + char* head=k_strdup(tmpbuf); + if(0) k_log_out("Actual request headers:\n%s", head); + k_channel_send(chan, head, ln, FREE_ON_SENT); +} + +/* -}{----------------------------------------------------------------------- */ + +char* get_request(char* header, k_hashtable* evt_head, k_hashtable* ent_head) +{ + char* at=header; + char* method=at; + at=strpbrk(at, WHITESPACEH); + if(!at) return 0; + *at++=0; + + k_hashtable_set(ent_head, "Method:", method); + + char* file=0; + char* host=0; + if(strcmp(method, "PING")){ + + at+=strspn(at, WHITESPACEH); + file=at; + at=strpbrk(at, WHITESPACEH); + if(!at) return 0; + *at++=0; + if(strlen(file) > 1024 ) return 0; + + if(!strncmp(file, "http://", 7)){ + char* s=file+7; + if(!*s || *s=='/') return 0; + char* e=strchr(s, '/'); + if(!e) return 0; + host=s; + *e++=0; + file=e; + } + else + if(*file=='/') file++; + else return 0; + + if(*file=='/'){ + char* s=file+1; + if(!*s || *s=='/') return 0; + char* e=strchr(s, '/'); + if(!e) return 0; + host=s; + *e++=0; + file=e; + } + } + if(file) k_hashtable_set(evt_head, "File:", file); + if(host) k_hashtable_set(evt_head, "Host:", host); + + at+=strspn(at, WHITESPACEH); + char* protocol=at; + at=strpbrk(at, WHITESPACE); + if(!at) at=protocol+strlen(protocol); + else *at++=0; + + k_hashtable_set(evt_head, "Protocol:", protocol); + + at+=strspn(at, WHITESPACE); + return at; +} + +char* get_response(char* header, k_hashtable* evt_head, k_hashtable* ent_head) +{ + char* at=header; + char* protocol=at; + at=strpbrk(at, WHITESPACEH); + if(!at) return 0; + *at++=0; + + k_hashtable_set(evt_head, "Protocol:", protocol); + + at+=strspn(at, WHITESPACEH); + char* status=at; + at=strpbrk(at, WHITESPACEH); + if(!at) return 0; + *at++=0; + + at+=strspn(at, WHITESPACEH); + char* statustext=at; + at=strpbrk(at, WHITESPACEV); + if(!at) return 0; + *at++=0; + + k_hashtable_set(ent_head, "Status:", status); + k_hashtable_set(ent_head, "Status-Text:", statustext); + + at+=strspn(at, WHITESPACE); + return at; +} + +char* get_val(char** atp) +{ + char* at=*atp; + char* val=at; + + val+=strspn(val, WHITESPACEH); + val+=strspn(val, WHITESPACEV); + + char* eov=end_of_val(at); + if(eov0; + if(localhostdns || localhostni || dotdotnumber){ + host="."; + } + } + else{ + host="."; + } + if(file){ + k_string_url_decode(file); + snprintf(tmpbuf, TMPBUFSIZE, "%s/%s", host, file); + k_hashtable_put_dup(ent_head, "Sub-To:", tmpbuf); + k_hashtable_set( ent_head, "Sub-Type:", "Original"); + } +} + +void init_headers(void) +{ + entity_headers =k_hashtable_new("Entity Headers", 1); + + k_hashtable_set(entity_headers, "URI:", (void*)1); + k_hashtable_set(entity_headers, "From:", (void*)1); + k_hashtable_set(entity_headers, "To:", (void*)1); + k_hashtable_set(entity_headers, "Via:", (void*)1); + k_hashtable_set(entity_headers, "Sub-To:", (void*)1); + k_hashtable_set(entity_headers, "Sub-Type:", (void*)1); + k_hashtable_set(entity_headers, "Method:", (void*)1); + k_hashtable_set(entity_headers, "Status:", (void*)1); + k_hashtable_set(entity_headers, "Status-Text:", (void*)1); + k_hashtable_set(entity_headers, "Last-Modified-Epoch:", (void*)1); + k_hashtable_set(entity_headers, "CUX:", (void*)1); + + k_hashtable_set(entity_headers, "Range:", (void*)1); + k_hashtable_set(entity_headers, "If-Modified-Since:", (void*)1); + k_hashtable_set(entity_headers, "If-None-Match:", (void*)1); + k_hashtable_set(entity_headers, "Cache-Control:", (void*)1); + k_hashtable_set(entity_headers, "Pragma:", (void*)1); + + k_hashtable_set(entity_headers, "Content-Length:", (void*)1); + k_hashtable_set(entity_headers, "Content-Type:", (void*)1); + k_hashtable_set(entity_headers, "Content-Encoding:", (void*)1); + k_hashtable_set(entity_headers, "Content-Location:", (void*)1); + k_hashtable_set(entity_headers, "Content-MD5:", (void*)1); + k_hashtable_set(entity_headers, "Content-Language:", (void*)1); + k_hashtable_set(entity_headers, "Content-Range:", (void*)1); + k_hashtable_set(entity_headers, "Last-Modified:", (void*)1); + k_hashtable_set(entity_headers, "ETag:", (void*)1); + k_hashtable_set(entity_headers, "Expires:", (void*)1); + + k_hashtable_set(entity_headers, "Allow:", (void*)1); +} + +void drop_entity_headers(k_hashtable* ent_head) +{ + k_hashtable_remove(ent_head, "Content-Length:"); + k_hashtable_remove(ent_head, "Content-Range:"); + k_hashtable_remove(ent_head, "Content-Type:"); + k_hashtable_remove(ent_head, "Content-Encoding:"); + k_hashtable_remove(ent_head, "Content-Location:"); + k_hashtable_remove(ent_head, "Last-Modified:"); +#ifdef DO_THESE_ONES_TOO + k_hashtable_remove(ent_head, "Allow:"); + k_hashtable_remove(ent_head, "Content-Language:"); + k_hashtable_remove(ent_head, "Content-MD5:"); + k_hashtable_remove(ent_head, "Expires:"); +#endif +} + +void fill_headers(k_hashtable* ent_head, + char* status, + char* statustext, + char* uri, + time_t modifitime, + int datalength, + char* mimetype, + char* encoding, + int nocache) +{ + if(status){ + k_hashtable_put_dup(ent_head, "Status:", status); + k_hashtable_put_dup(ent_head, "Status-Text:", statustext); + } + if(uri){ + k_hashtable_put_dup(ent_head, "URI:", uri); + } + if(modifitime>=0){ + snprintf(tmpbuf, TMPBUFSIZE, "%s", k_time_to_rfc(modifitime)); + k_hashtable_put_dup(ent_head, "Last-Modified:", tmpbuf); + } + if(datalength>=0){ + snprintf(tmpbuf, TMPBUFSIZE, "%d", datalength); + k_hashtable_put_dup(ent_head, "Content-Length:", tmpbuf); + } + if(mimetype){ + k_hashtable_put_dup(ent_head, "Content-Type:", mimetype); + } + if(encoding){ + k_hashtable_put_dup(ent_head, "Content-Encoding:", encoding); + } + if(nocache==1){ + k_hashtable_put_dup(ent_head, "Cache-Control:", + "no-cache,no-store"); + } + else + if(nocache==0){ + long maxage=31449600; + snprintf(tmpbuf, TMPBUFSIZE, "max-age=%ld", maxage); + char* maxages=k_time_to_rfc_relative(maxage); + k_hashtable_put_dup(ent_head, "Cache-Control:", tmpbuf); + k_hashtable_put_dup(ent_head, "Expires:", maxages); + } + else + if(nocache== -1){ + k_hashtable_remove(ent_head, "Cache-Control:"); + k_hashtable_remove(ent_head, "Expires:"); + } +} + +/* -}{---- ------------------------------------------------------------------ */ + diff --git a/src/ni/ni.c b/src/ni/ni.c new file mode 100644 index 0000000..1f0a7ef --- /dev/null +++ b/src/ni/ni.c @@ -0,0 +1,783 @@ + +/* -}{----------------------------------------------------------------------- */ + +#include +#undef PUBLIC +#define PUBLIC EXPORT +#include + +/* -}{---- ------------------------------------------------------------------ */ + +#define TMPBUFSIZE 4096 +static char tmpbuf[TMPBUFSIZE]; +static char* hostname; +static k_hashtable* resources; + +/* -}{---- ------------------------------------------------------------------ */ + +typedef struct ni_driver{ + char* name; + ni_handles_resource handles_resource; + ni_sync_resource sync_resource; +} ni_driver; + +/* -}{---- From headers.c --------------------------------------------------- */ + +extern void init_headers(void); +extern void drop_entity_headers(k_hashtable* ent_head); +extern void fill_headers(k_hashtable* ent_head, + char* status, + char* statustext, + char* uri, + time_t modifitime, + int datalength, + char* mimetype, + char* encoding, + int nocache); + +/* -}{---- ------------------------------------------------------------------ */ + +static void incoming_request(ni_event* evq); +static ni_resource* ensure_res(char* pub); +static void ensure_sub_entry(k_hashtable* ent_head, k_hashtable* sub); +static void ensure_pub_entry(k_hashtable* ent_head, k_hashtable* sub); +static k_hashtable* get_this(k_hashtable*, char*, k_hashtable*); +static k_hashtable* get_using(k_hashtable* curr, char* tag, char* val); +static void post_to_driver(char* pub, ni_event* evq); +static void incoming_resource(ni_event* evt); +static void test_pub_tos(ni_resource* res, ni_event* evt, int entityok); +static int satisfiable(k_hashtable*, ni_resource*, ni_event*, int); +static void update_pubcache(int, k_hashtable**, k_hashtable*, k_hashtable*); +static int sub_less(k_hashtable* sub1, k_hashtable* sub2); +static void fix_via_subs(k_hashtable* evteh, k_hashtable* reseh); +static int sub_ok(k_hashtable* sub); +static int range_ok(char* range, char* conrange); +static void merge_non_entity_headers(k_hashtable* reseh, k_hashtable* evteh); +static void merge_entity_range(ni_resource* res, ni_event* evt); +static void respond( k_hashtable* sub, ni_resource* res, ni_event* evt); +static void respond_ok(k_hashtable* sub, ni_event* evv); +static void respond_nf(k_hashtable* sub, ni_event* evv); +static void* entity_to_octets(k_hashtable* ent_head, void* entity); +static char* handled_by(char* pub); +static void call_sync_resource(ni_resource* res); +static ni_driver* ni_driver_new(char* name, + ni_handles_resource handles_resource, + ni_sync_resource sync_resource); + +/* -}{---- ------------------------------------------------------------------ */ + +EXPORT int ni_module_loaded(void) +{ + resources=k_hashtable_new("Resources", 0); + + init_headers(); + + k_log_out("NI initialised"); + return 1; +} + +EXPORT void ni_module_tick(void) +{ +} + +EXPORT int ni_module_event(void* data) +{ + ni_event* evt=data; + + if(!k_hashtable_get(evt->ent_head, "Status:")){ + + incoming_request(evt); + } + else{ + incoming_resource(evt); + } + return 1; +} + +/* -}{---- ------------------------------------------------------------------ */ + +void incoming_request(ni_event* evq) +{ + int L=1; + if(L) ni_event_show(evq, "NI got request event"); + + k_hashtable* sub=evq->ent_head; + char* uri =k_hashtable_get(sub, "URI:"); + char* pub =k_hashtable_get(sub, "Sub-To:"); if(!pub) return; + int styor=k_hashtable_isi(sub, "Sub-Type:", "Original"); + char* via =k_hashtable_get(sub, "Via:"); + char* from =k_hashtable_get(sub, "From:"); + if(L) k_log_out("------------------ %s", pub); + + int nocache=k_hashtable_isi(sub, "Cache-Control:", "no-cache"); + if(nocache){ + k_hashtable_remove( sub, "Cache-Control:"); + k_hashtable_set( sub, "If-Modified-Since:", "0"); + } + + if(!from){ + ni_resource* ses=k_hashtable_get(resources, uri); + if(ses) ensure_sub_entry(ses->ent_head, sub); + if(ses) if(L) ni_resource_show(ses, "Subscribing Resource:"); + if(styor && via){ + post_to_driver(pub, evq); + return; + } + } + + ni_resource* res=ensure_res(pub); + if(L) ni_resource_show(res, "Publishing Resource:"); + + k_hashtable* pubcache=0; + int sat=satisfiable(sub, res, 0, 0); + update_pubcache(sat, &pubcache, res->ent_head, sub); + if(sat){ + if(L) k_log_out("Memory cache hit for %s", pub); + respond(k_hashtable_dup(sub), res, 0); + ni_event_delete(evq); + } + else{ + ensure_pub_entry(res->ent_head, sub); + if(L) ni_resource_show(res, "Pending Resource:"); + if(pubcache){ + evq->ent_head=pubcache; + post_to_driver(pub, evq); + k_hashtable_delete(sub); + } + else{ + if(L) k_log_out("In-progress Pub-Cache sufficient"); + ni_event_delete(evq); + } + } +} + +ni_resource* ensure_res(char* pub) +{ + ni_resource* res=k_hashtable_get(resources, pub); + if(!res){ + res=ni_resource_new(pub, k_hashtable_new("resHeaders", 1), 0); + k_hashtable_set(resources, pub, res); + } + return res; +} + +void ensure_sub_entry(k_hashtable* ent_head, k_hashtable* sub) +{ + char* tag="Sub-To:"; + k_hashtable* sup=get_this(ent_head, tag, sub); + if(!sup){ + k_hashtable* sup=k_hashtable_dup(sub); + char* uri=k_hashtable_extract(sup, "Sub-To:"); + k_hashtable_put( sup, "URI:", uri); + k_hashtable_remove( sup, "Sub-Type:"); + k_hashtable_sub(ent_head, tag, sup); + } + else{ + k_hashtable_remove(sup, "Range:"); + k_hashtable_remove(sup, "Content-Range:"); + k_hashtable_remove(sup, "Status:"); + k_hashtable_remove(sup, "Status-Cache:"); + char* mth=k_strdup(k_hashtable_get(sub, "Method:")); + char* via=k_strdup(k_hashtable_get(sub, "Via:")); + char* ims=k_strdup(k_hashtable_get(sub, "If-Modified-Since:")); + char* rng=k_strdup(k_hashtable_get(sub, "Range:")); + if(mth) k_hashtable_put(sup, "Method:", mth); + if(via) k_hashtable_put(sup, "Via:", via); + if(ims) k_hashtable_put(sup, "If-Modified-Since:", ims); + if(rng) k_hashtable_put(sup, "Range:", rng); + } +} + +void ensure_pub_entry(k_hashtable* ent_head, k_hashtable* sub) +{ + char* tag="Pub-To:"; + k_hashtable* sup=get_this(ent_head, tag, sub); + if(!sup){ + k_hashtable* sup=k_hashtable_dup(sub); + k_hashtable_remove(sup, "Sub-To:"); + k_hashtable_remove(sup, "Sub-Type:"); + k_hashtable_sub(ent_head, tag, sup); + } + else { + k_hashtable_remove(sup, "Method:"); + k_hashtable_remove(sup, "If-Modified-Since:"); + k_hashtable_remove(sup, "Cache-Control:"); + k_hashtable_remove(sup, "Update:"); + char* mth=k_strdup(k_hashtable_get(sub, "Method:")); + char* ims=k_strdup(k_hashtable_get(sub, "If-Modified-Since:")); + char* ccn=k_strdup(k_hashtable_get(sub, "Cache-Control:")); + char* upd=k_strdup(k_hashtable_get(sub, "Update:")); + if(mth) k_hashtable_put(sup, "Method:", mth); + if(ims) k_hashtable_put(sup, "If-Modified-Since:", ims); + if(ccn) k_hashtable_put(sup, "Cache-Control:", ccn); + if(upd) k_hashtable_put(sup, "Update:", upd); + } +} + +k_hashtable* get_this(k_hashtable* ent_head, char* pubsub, k_hashtable* try) +{ + k_hashtable* curr=k_hashtable_get(ent_head, pubsub); + if(!curr) return 0; + + char* tag; + char* val; + + tag="From:"; + val=k_hashtable_get(try, tag); + if(val) return get_using(curr, tag, val); + + tag="URI:"; + val=k_hashtable_get(try, tag); + if(val) return get_using(curr, tag, val); + + return 0; +} + +k_hashtable* get_using(k_hashtable* curr, char* tag, char* val) +{ + k_hashtable* c; + for(c=curr; c; c=c->next) if(k_hashtable_is(c, tag, val)) return c; + return c; +} + +/* -}{---- ------------------------------------------------------------------ */ + +void incoming_resource(ni_event* evt) +{ + int L=0; + if(L) ni_event_show(evt, "ni got resource incoming event"); + + ni_resource* res=k_hashtable_get(resources, evt->uri); + int fullconst=res && k_hashtable_is(res->ent_head, "Status:", "200") && + k_hashtable_is(res->ent_head, "CUX:", "C"); + + if(fullconst){ + k_log_err("Resource is complete and Constant"); + ni_event_delete(evt); + return; + } + if(!res) res=ensure_res(evt->uri); + + k_hashtable* reseh=res->ent_head; + k_hashtable* evteh=evt->ent_head; + int okfull =k_hashtable_is(evteh, "Status:", "200"); + int partial =k_hashtable_is(evteh, "Status:", "206"); + int headonly=k_hashtable_is(evteh, "Status:", "260"); + int updated =k_hashtable_is(evteh, "Status:", "266"); + int notmod =k_hashtable_is(evteh, "Status:", "304"); + int notfound=k_hashtable_is(evteh, "Status:", "404"); + + int entityok= (okfull || partial || notmod); + int entityev=!(headonly || notmod || notfound); + int entityin=!!evt->entity; + int entityon=!!res->entity; + + if(!entityev){ + } + if(okfull || !entityon){ + k_hashtable_merge(reseh, evteh); + if(okfull) k_hashtable_remove(reseh, "Content-Range:"); + } + else{ + merge_non_entity_headers(reseh, evteh); + } + + fix_via_subs(evteh, reseh); + + if(entityin){ + if(okfull || (partial && !entityon)){ + if(res->entity!=evt->entity){ + if(!k_hashtable_is(reseh, "CUX:", "C")){ + k_free(res->entity); + } + res->entity=evt->entity; + } + } + else + if(partial && entityon){ + merge_entity_range(res, evt); + } + } + + if(L) ni_resource_show(res, "updated resource - sync and try pubs:"); + + call_sync_resource(res); + + test_pub_tos(res, evt, entityok); + + evt->entity=0; + ni_event_delete(evt); + + if(L) ni_resource_show(res, "resource after ni:"); +} + +void fix_via_subs(k_hashtable* evteh, k_hashtable* reseh) +{ + char* from=k_hashtable_get(evteh, "From:"); + if(!from) return; + k_hashtable* subs=k_hashtable_get(reseh, "Sub-To:"); + k_hashtable* sub; + for(sub=subs; sub; sub=sub->next){ + if(!k_hashtable_is(sub, "Via:", from)) continue; + int st=k_hashtable_get_int(evteh, "Status:"); + char* cr=k_hashtable_get_dup(evteh, "Content-Range:"); + if(st!=304) k_hashtable_put_int(sub, "Status:", st); + if(st==200) k_hashtable_remove( sub, "Content-Range:"); + else + if(cr) k_hashtable_put(sub, "Content-Range:", cr); + if(sub_ok(sub)) k_hashtable_set(sub, "Status-Cache:", "OK"); + } +} + +void test_pub_tos(ni_resource* res, ni_event* evt, int entityok) +{ + k_hashtable* keeps=0; + k_hashtable* sub=k_hashtable_extract(res->ent_head, "Pub-To:"); + while(sub){ + k_hashtable* subnext=sub->next; + sub->next=0; + int keep=1; + int sat=satisfiable(sub, res, evt, entityok); + update_pubcache(sat, 0, res->ent_head, sub); + if(sat){ + respond(k_hashtable_dup(sub), res, evt); + if(!k_hashtable_get(sub, "Update:")) keep=0; + } + if(keep){ + sub->next=keeps; + keeps=sub; + } + else{ + k_hashtable_delete(sub); + } + sub=subnext; + } + sub=keeps; + while(sub){ + k_hashtable* subnext=sub->next; + k_hashtable_sub(res->ent_head, "Pub-To:", sub); + sub=subnext; + } +} + +int satisfiable(k_hashtable* sub, + ni_resource* res, + ni_event* evt, + int entityok) +{ + int L=0; + int get =k_hashtable_is( sub, "Method:", "GET"); + int head =k_hashtable_is( sub, "Method:", "HEAD"); + int dosub=k_hashtable_is( sub, "Method:", "SUB"); + int unsub=k_hashtable_is( sub, "Method:", "UNSUB"); + int full=!k_hashtable_isi(sub, "Cache-Control:", "no-full"); + int updt =k_hashtable_isi(sub, "Update:", "changes"); + char* ims =k_hashtable_get(sub, "If-Modified-Since:"); + char* range=k_hashtable_get(sub, "Range:"); + + int getsub =get || dosub; + int getrange=getsub && range; + + k_hashtable* reseh=res->ent_head; + int gotall= k_hashtable_is( reseh, "Status:", "200"); + int gotrange= k_hashtable_is( reseh, "Status:", "206"); + int gothead= k_hashtable_is( reseh, "Status:", "260"); + int gotupdated=evt && + k_hashtable_is(evt->ent_head, "Status:", "266"); + int notfound= k_hashtable_is( reseh, "Status:", "404"); + int constant= k_hashtable_is( reseh, "CUX:", "C" ); + char* conrange= k_hashtable_get(reseh, "Content-Range:"); + + if(unsub) return 0; + if(notfound) return 1; + + int gotany=(gothead || gotrange || gotall); + int snapok=(constant || !ims || entityok); + + int sat= + (full && + ((getsub && gotall && snapok) || + (getrange && gotrange && range_ok(range, conrange) && snapok) || + (head && gotany ) )) + || + (updt && + ((dosub && gotupdated))); + + if(L) k_log_out("satisfiable %d", sat); + return sat; +} + +void update_pubcache(int sat, + k_hashtable** pubcachep, + k_hashtable* reseh, + k_hashtable* sub) +{ + if(!sat){ if(pubcachep){ + k_hashtable* pubcache; + pubcache=k_hashtable_get(reseh, "Pub-Cache:"); + if(!pubcache || sub_less(pubcache, sub)){ + if(!pubcache){ + pubcache=k_hashtable_dup(sub); + k_hashtable_sub(reseh, "Pub-Cache:", pubcache); + } + else{ + k_hashtable_merge(pubcache, sub); + } + k_hashtable_remove(pubcache, "URI:"); + k_hashtable_remove(pubcache, "Sub-Type:"); + *pubcachep=k_hashtable_dup(pubcache); + k_hashtable_set(*pubcachep, "Sub-Type:", "Cache"); + } + }} + else{ if(!pubcachep){ + k_hashtable* pubcache; + pubcache=k_hashtable_get(reseh, "Pub-Cache:"); + if(!sub_less(sub, pubcache)){ + k_hashtable_remove(pubcache, "Method:"); + k_hashtable_remove(pubcache, "If-Modified-Since:"); + } + }} +} + +int sub_ok(k_hashtable* sub) +{ + char* gotresp =k_hashtable_get(sub, "Status:"); + if(!gotresp) return 0; + int notfound=k_hashtable_is( sub, "Status:", "404"); + if(notfound) return 0; + int gotall =k_hashtable_is( sub, "Status:", "200"); + int gotrange=k_hashtable_is( sub, "Status:", "206"); + int gothead= k_hashtable_is( sub, "Status:", "260"); + int doget =k_hashtable_is( sub, "Method:", "GET"); + int dohead =k_hashtable_is( sub, "Method:", "HEAD"); + int dosub =k_hashtable_is( sub, "Method:", "SUB"); + char* range =k_hashtable_get(sub, "Range:"); + char* conrange=k_hashtable_get(sub, "Content-Range:"); + + int dogetsub=doget || dosub; + int dogetrange=dogetsub && range; + int gotany =(gothead || gotrange || gotall); + + return (dogetsub && gotall ) || + (dogetrange && gotrange && range_ok(range, conrange)) || + (dohead && gotany ); +} + +int sub_less(k_hashtable* sub1, k_hashtable* sub2) +{ + int L=0; + if(L) k_log_out("sub_less sub1:"); + if(L) k_hashtable_show_chars(sub1); + if(L) k_log_out("sub_less sub2:"); + if(L) k_hashtable_show_chars(sub2); + char* mth1 =k_hashtable_get(sub1, "Method:"); + int head1=k_hashtable_is( sub1, "Method:", "HEAD"); + int get2 =k_hashtable_is( sub2, "Method:", "GET"); + char* ims1 =k_hashtable_get(sub1, "If-Modified-Since:"); + char* ims2 =k_hashtable_get(sub2, "If-Modified-Since:"); + int full=!k_hashtable_isi(sub2, "Cache-Control:", "no-full"); + return full && ((!mth1) || (head1 && get2) || (!ims1 && ims2)); +} + +int range_ok(char* range, char* conrange) +{ + k_log_out("range_ok not implemented: %s vs %s", range, conrange); + return 0; +} + +void merge_non_entity_headers(k_hashtable* reseh, k_hashtable* evteh) +{ +} + +void merge_entity_range(ni_resource* res, ni_event* evt) +{ + k_log_out("merge_entity_range not implemented yet"); +} + +/* -}{---- ------------------------------------------------------------------ */ + +void respond(k_hashtable* sub, ni_resource* res, ni_event* evt) +{ + int updated=evt && k_hashtable_is(evt->ent_head, "Status:", "266"); + ni_event* evv=updated? ni_event_dup(evt): + ni_res_to_evt(res); + int L=0; + if(L) ni_event_show(evv, "respond"); + + k_hashtable_remove(evv->ent_head, "Permit:"); + k_hashtable_remove(evv->ent_head, "Sub-To:"); + k_hashtable_remove(evv->ent_head, "Pub-To:"); + k_hashtable_remove(evv->ent_head, "Pub-Cache:"); + k_hashtable_sub( evv->ent_head, "Pub-To:", sub); + + int nf; + nf=k_hashtable_is( evv->ent_head, "Status:", "404"); + if(!nf) respond_ok(sub, evv); + else respond_nf(sub, evv); +} + +void respond_ok(k_hashtable* sub, ni_event* evv) +{ + char* status=0; + char* statustext=0; + int datalength= -1; + int nocache=0; + k_hashtable* ent_head=evv->ent_head; + + time_t modifitime=k_hashtable_get_int(ent_head, "Last-Modified-Epoch:"); + char* modistring=k_hashtable_get( ent_head, "Last-Modified:"); + if(!modifitime){ + modifitime=k_time_from_rfc(modistring); + } + char* imss=k_hashtable_get(sub, "If-Modified-Since:"); + time_t imst=k_time_from_rfc(imss); + + if(modifitime== -1 || imst== -1 || modifitime > imst){ + if(k_hashtable_is(sub, "Method:", "HEAD")){ + evv->entity=0; + } + if(!k_hashtable_is(ent_head, "CUX:", "C")){ + evv->entity=entity_to_octets(ent_head, evv->entity); + nocache=1; + } + } + else{ + status="304"; + statustext="Not Modified"; + modifitime= -1; + nocache= -1; + evv->entity=0; + drop_entity_headers(ent_head); + } + + fill_headers(ent_head, status, statustext, 0, + modistring? -1: modifitime, + datalength, 0, 0, nocache); + + char* pub =k_hashtable_get(sub, "URI:"); + int L=0; + if(L) ni_event_show(evv, "respond_ok"); + post_to_driver(pub, evv); +} + +void respond_nf(k_hashtable* sub, ni_event* evv) +{ + fill_headers(evv->ent_head, "404", "File Not Found", 0, -1, 0, 0, 0, 1); + char* pub =k_hashtable_get(sub, "URI:"); + post_to_driver(pub, evv); +} + +void post_to_driver(char* pub, ni_event* evq) +{ + char* driver=handled_by(pub); + if(0) k_log_out("Passing %s on to %s", pub, driver); + if(0) ni_event_show(evq, "Post to Driver:"); + k_event_post(driver, evq); +} + +void* entity_to_octets(k_hashtable* ent_head, void* entity) +{ + if(!entity) return 0; + size_t size=k_hashtable_get_int(ent_head, "Content-Length:"); + return k_memdup(entity, size); +} + +/* -}{---- ------------------------------------------------------------------ */ + +EXPORT char* ni_hostname() +{ + return hostname? hostname: "not set"; +} + +EXPORT void ni_hostname_set(char* name) +{ + k_free(hostname); + hostname=k_strdup(name); +} + +/* -}{---- ------------------------------------------------------------------ */ + +static ni_driver* npdriver; +static ni_driver* moddriver; + +char* handled_by(char* pub) +{ + if(moddriver->handles_resource(pub)) return moddriver->name; + return "np"; +} + +void call_sync_resource(ni_resource* res) +{ + if(moddriver->handles_resource(res->uri)) moddriver->sync_resource(res); + else npdriver->sync_resource(res); +} + +ni_driver* ni_driver_new(char* name, + ni_handles_resource handles_resource, + ni_sync_resource sync_resource) +{ + ni_driver* driver=k_malloc(sizeof(ni_driver)); + driver->name =name; + driver->handles_resource=handles_resource; + driver->sync_resource =sync_resource; + return driver; +} + +EXPORT void ni_register_driver(char* name, + ni_handles_resource handles_resource, + ni_sync_resource sync_resource) +{ + ni_driver* d=ni_driver_new(name, handles_resource, sync_resource); + if(!strcmp(name, "np")) npdriver=d; + else moddriver=d; +} + +/* -}{---- ------------------------------------------------------------------ */ + +EXPORT ni_resource* ni_resource_new(char* uri, + k_hashtable* ent_head, + char* entity) +{ + char* urih=k_hashtable_get(ent_head, "URI:"); + if(urih) uri=urih; + else + if(uri) k_hashtable_put_dup(ent_head, "URI:", uri); + + ni_resource* res=k_malloc(sizeof(ni_resource)); + res->uri =(uri? k_strdup(uri): 0); + res->ent_head=ent_head; + res->entity =entity; + return res; +} + +EXPORT ni_resource* ni_resource_dup(ni_resource* res) +{ + ni_resource* rep=k_malloc(sizeof(ni_resource)); + rep->uri =k_strdup( res->uri); + rep->ent_head=k_hashtable_dup(res->ent_head); + rep->entity = res->entity; + return rep; +} + +EXPORT void ni_resource_delete(ni_resource* res) +{ + if(!res) return; + if(res->entity && res->ent_head){ + int constant=k_hashtable_is(res->ent_head, "CUX:", "C"); + if(!constant) k_free(res->entity); + } + k_hashtable_delete(res->ent_head); + k_free( res->uri); + k_free( res); +} + +static char* excto[]={ "Permit:", "Sub-To:", "Pub-To:", 0 }; +static char* perto[]={ "Permit:", 0 }; +static char* subto[]={ "Sub-To:", 0 }; +static char* pubto[]={ "Pub-To:", 0 }; + +EXPORT void ni_resource_show(ni_resource* res, char* text) +{ + if(!res){ + k_log_out("\n---%s--------\n------------\n\n----------", + text); + } + else{ + char* b=tmpbuf; + size_t s=TMPBUFSIZE; + size_t l=0; + l+=k_hashtable_snprintf_x(res->ent_head, b+l, s-l, excto); + l+=k_hashtable_snprintf_i(res->ent_head, b+l, s-l, perto); + l+=k_hashtable_snprintf_i(res->ent_head, b+l, s-l, subto); + l+=k_hashtable_snprintf_i(res->ent_head, b+l, s-l, pubto); + k_log_out("\n---%s----%s--\n%s----------\n%p\n----------", + text, res->uri, tmpbuf, res->entity); + } +} + +/* -}{---- ------------------------------------------------------------------ */ + +EXPORT ni_resource* ni_resource_get(char* uri) +{ + return k_hashtable_get(resources, uri); +} + +/* -}{---- ------------------------------------------------------------------ */ + +EXPORT ni_event* ni_res_to_evt(ni_resource* res) +{ + ni_event* evp=k_malloc(sizeof(ni_event)); + evp->uri =k_strdup( res->uri); + evp->evt_head=k_hashtable_new("vHeaders/ni_res_to_evt", 1); + evp->ent_head=k_hashtable_dup(res->ent_head); + evp->entity = res->entity; + return evp; +} + +/* -}{---- ------------------------------------------------------------------ */ + +EXPORT ni_event* ni_event_new(char* uri, + k_hashtable* evt_head, + k_hashtable* ent_head, + char* entity) +{ + char* urih=k_hashtable_get(ent_head, "URI:"); + if(urih) uri=urih; + else + if(uri) k_hashtable_put_dup(ent_head, "URI:", uri); + + ni_event* evt=k_malloc(sizeof(ni_event)); + evt->uri =(uri? k_strdup(uri): 0); + evt->evt_head=evt_head; + evt->ent_head=ent_head; + evt->entity =entity; + return evt; +} + +EXPORT ni_event* ni_event_dup(ni_event* evt) +{ + ni_event* evp=k_malloc(sizeof(ni_event)); + evp->uri =k_strdup( evt->uri); + evp->evt_head=k_hashtable_dup(evt->evt_head); + evp->ent_head=k_hashtable_dup(evt->ent_head); + evp->entity = evt->entity; + return evp; +} + +EXPORT void ni_event_delete(ni_event* evt) +{ + if(!evt) return; + if(evt->entity && evt->ent_head){ + int constant=k_hashtable_is(evt->ent_head, "CUX:", "C"); + if(!constant) k_free(evt->entity); + } + k_hashtable_delete(evt->evt_head); + k_hashtable_delete(evt->ent_head); + k_free( evt->uri); + k_free( evt); +} + +EXPORT void ni_event_show(ni_event* evt, char* text) +{ + if(!evt){ + k_log_out("\n---%s--------\n------------\n\n----------", + text); + } + else{ + char* buf=tmpbuf; + int siz=TMPBUFSIZE; + int n=k_hashtable_snprintf(evt->evt_head, buf, siz)+1; + ; k_hashtable_snprintf(evt->ent_head, buf+n, siz-n); + k_log_out("\n---%s----%s--\n" + "%s----------\n" + "%s----------\n" + "%p\n----------", + text, evt->uri, buf, buf+n, evt->entity); + } +} + +/* -}{----------------------------------------------------------------------- */ + + + + diff --git a/src/platform/container.h b/src/platform/container.h new file mode 100644 index 0000000..adb9cce --- /dev/null +++ b/src/platform/container.h @@ -0,0 +1,19 @@ +/* -------------------------------------------------------------------------- */ +/* + container.h: lifecycle and external event functions + from executable to kernel +*/ + +PUBLIC void c_init(CONSOLE console, + char* version, + char* ciux, + void (*terminate)(void)); + +PUBLIC C_RUN_RV c_run(C_RUN_ARG arg); + +PUBLIC void c_running(int running); +PUBLIC void c_key(unsigned char key, int down); +PUBLIC void c_signal(int signum); + +/* -------------------------------------------------------------------------- */ + diff --git a/src/platform/kernelapi.c b/src/platform/kernelapi.c new file mode 100644 index 0000000..86c406b --- /dev/null +++ b/src/platform/kernelapi.c @@ -0,0 +1,2053 @@ + +/* -------------------------------------------------------------------------- */ + +#define PUBLIC EXPORT +#include +#include +#include "container.h" + +/* -------------------------------------------------------------------------- */ + +#define OTHER_THREAD +#define CERNDATE "%d/%b/%Y:%H:%M:%S" +#define RDSIZE 40960 +#define WRSIZE 2 +#define TMPBUFSIZE 40960 +#define WRITEMAX 30000 +#define LOGBUFSIZE 1024 +#define OOM_STRING "Out of memory!\nBailing." +#define MAX_MODULES 64 +#define MAX_EVENTS_PER_MODULE 3 +#define CHAN_CLOSING(chan) (chan->priv->state!=CHAN_OPEN) +#define CHAN_CLOSE 0 +#define CHAN_LINGER 1 +#define CHAN_OPEN 2 +#define SETCB_RD 1 +#define SETCB_WR 2 +#define MIN(a,b) (((a) < (b))? (a): (b)) + +/* -------------------------------------------------------------------------- */ + +typedef struct wrbuffer{ + char* base; + size_t size; + char* free; +} wrbuffer; + +struct k_channel_priv{ + + SOCK_T SOCK; + int state; + int event; + int rwmask; + char* rdbuffer; + size_t rdbufpos; + size_t rdbufsize; + int rdbufforeign; + wrbuffer* wrbuffers; + int wrbufpos; + int wrbufsize; + int lingercount; +}; + +typedef struct event{ struct event* next; + void* data; +} event; + +typedef struct k_module{ + char* name; + k_module_tick_fn tick_fn; + k_module_event_fn event_fn; + event* queue; +} k_module; + +typedef struct key_event{ struct key_event* next; + unsigned char key; + int down; +} key_event; + +/* -------------------------------------------------------------------------- */ + +static int running=2; +static k_gl_reshape_event reshape_fn=0; +static k_gl_draw_event draw_fn=0; +static k_gl_key_event key_fn=0; +static k_module modules[MAX_MODULES]; +static int nextmodule=0; +static key_event* key_consumer=0; +static key_event* key_producer=0; +static k_channel* k_channels=0; +static k_hashtable* connecting_chans; +static k_hashtable* current_chans; +static k_hashtable* monthhash; +static char tmpbuf[TMPBUFSIZE]; +static char logbuf[LOGBUFSIZE]; +static FILEP logfile=0; +static int log_gmt=0; +static char dummy_empty_data[0]; + +/* -------------------------------------------------------------------------- */ + +static void gen_conn_name(k_channel* chan); +static void chan_closed(k_channel* chan); +static SOCK_T make_listen_socket(int listenport); +static SOCK_T make_connect_socket(void); +static int listen_socket(SOCK_T s, int listenport); +static int connect_socket(SOCK_T s, char*, int); +static void add_module_entry(char*, k_module_tick_fn, k_module_event_fn); +static int run_queues(void); +static void show_open_channels(void); +static k_channel* find_k_channel_for_socket(SOCK_T s); +static k_channel* register_k_channel(char* name, + char* listenhost, + int listenport, + char* banner, + k_channel_event rdcallback, + k_channel_event wrcallback, + void* context, + int type, + struct in_addr clientip, + SOCK_T s); +static event** get_module_queue(char* name); +static k_module_loaded_fn get_module_loaded_fn(char* name, MODULE module); +static k_module_tick_fn get_module_tick_fn( char* name, MODULE module); +static k_module_event_fn get_module_event_fn( char* name, MODULE module); +static int check_i(char* key, char* includes[], int ignorecase); +static int check_x(char* key, char* excludes[], int ignorecase); +static unsigned int string_hash(char* p); +static int set_rdbuffer(k_channel*, char* rdbuffer, size_t size); +static char* get_rdbuffer(k_channel* chan); +static void append_to_wrbuffers(k_channel*, char*, size_t, int); +static char* chop_from_rdbuffer_div(k_channel* chan, char* div); +static char* chop_from_rdbuffer_len(k_channel* chan, size_t size); +static void chop_from_wrbuffers( k_channel* chan, size_t size); +static void got_key(unsigned char key, int down); +static void init_keys(void); +static void dir_read(char*, char*, char*, int, k_stat, void*); +static void load_module(char* name, int dot); +static void init_modules(void); +static void log_net_err(char* where, int e); +static int dir_list(char*, int, char*, char*, char*); +static int ensure_dir(char* fullname); +static void next_keys(void); +static void read_more_and_notify(k_channel* chan); +static void readable_socket(k_channel* chan); +static void register_connections(k_channel* chan); +static void do_regular_things(void); +static void tick_modules(void); +static void remove_dead_k_channels(void); +static void unregister_k_channel(k_channel* chan, int now); +static void write_more_and_notify(k_channel* chan); +static char hex_to_int(char c); +static void write_to_logfile_cern_style(char* text, int error); +static void writeable_socket(k_channel* chan); +static void exception_socket(k_channel* chan); +static SOCK_T make_udp_listen_socket(int listenport); +static struct sockaddr_in make_sockaddr_in(unsigned long address, + unsigned int port); +static void init_hashtables(void); + +/* -------------------------------------------------------------------------- */ + +#include + +/* -------------------------------------------------------------------------- */ + +EXPORT void c_init(CONSOLE console, + char* version, + char* ciux, + void (*terminate)(void)) +{ + k_console =console; + k_version =version; + k_ciux =ciux; + k_terminate=terminate; + + init_hashtables(); + init_keys(); + + if(LOAD_MODULES_EARLY){ + init_gl(); + init_net(); + init_modules(); + } +} + +EXPORT C_RUN_RV c_run(C_RUN_ARG arg) +{ + init_thread(); + + if(!LOAD_MODULES_EARLY){ + init_gl(); + init_net(); + init_modules(); + } + + if(reshape_fn)(*reshape_fn)(640,480); + if(draw_fn) (*draw_fn)(); + + k_log_out("Cilux U-Web Exchange started"); + k_log_out("%s", k_version); + k_log_out("---------------------------------------------"); + + int queue_events_to_do=0; + while(1){ + if(running==2) poller(queue_events_to_do); + if(running==1) SLEEP_MS(LOOP_TICK); + if(running==0) break; + + if(running==2) queue_events_to_do=run_queues(); + if(running==1) ; + if(running==0) break; + } + + k_log_out("Cilux U-Web Exchange terminated"); + + return 0; +} + +EXPORT OTHER_THREAD void c_running(int r) +{ + running=r; +} + +EXPORT OTHER_THREAD void c_key(unsigned char key, int down) +{ + got_key(key, down); +} + +EXPORT OTHER_THREAD void c_signal(int signum) +{ + k_log_out("Received signal %d", signum); +} + +/* -------------------------------------------------------------------------- */ + +EXPORT void k_gl_register_reshape(k_gl_reshape_event callback){ + reshape_fn=callback; +} + +EXPORT void k_gl_register_draw(k_gl_draw_event callback){ + draw_fn=callback; +} + +EXPORT void k_gl_register_key(k_gl_key_event callback){ + key_fn=callback; +} + +/* -------------------------------------------------------------------------- */ + +void init_hashtables(void) +{ + connecting_chans=k_hashtable_new("Connecting Channels", 0); + current_chans =k_hashtable_new("Current Channels", 0); + monthhash =k_hashtable_new("Months", 1); + k_hashtable_set(monthhash, "Jan", (void*)1); + k_hashtable_set(monthhash, "Feb", (void*)2); + k_hashtable_set(monthhash, "Mar", (void*)3); + k_hashtable_set(monthhash, "Apr", (void*)4); + k_hashtable_set(monthhash, "May", (void*)5); + k_hashtable_set(monthhash, "Jun", (void*)6); + k_hashtable_set(monthhash, "Jul", (void*)7); + k_hashtable_set(monthhash, "Aug", (void*)8); + k_hashtable_set(monthhash, "Sep", (void*)9); + k_hashtable_set(monthhash, "Oct", (void*)10); + k_hashtable_set(monthhash, "Nov", (void*)11); + k_hashtable_set(monthhash, "Dec", (void*)12); +} + +void init_logging(void) +{ + snprintf(tmpbuf, TMPBUFSIZE, "%s.log", k_ciux); + logfile=FOPEN(tmpbuf, "a"); + if(!logfile) k_log_err("couldn't open logfile %s", tmpbuf); +} + +void init_modules(void) +{ + char* patts[]={ "{*.dll,*.so}", 0}; + char* temps[]={ "%s:", 0 }; + k_dir_list(".", ".", patts, temps, dir_read, 0); +} + +void dir_read(char* basedir, + char* path, + char* data, + int usedmmap, + k_stat kstat, + void* context) +{ + int is_np=0; + if(data){ + if(!strncmp(data, MODPRE, strlen(MODPRE))){ + k_ciux=k_strdup(data+strlen(MODPRE)); + char* c=strchr(k_ciux, '.'); + *c=0; + } + else{ + is_np=1; + k_ciux="np"; + } + k_free(data); + } + + init_logging(); + + k_log_out("========================================"); + + load_module("ni", 0); + load_module("np", 0); + if(!is_np) load_module(k_ciux, 1); + + k_log_out("========================================"); +} + +void load_module(char* name, int dot) +{ + k_log_out("------------ %s --------------", name); + char dlname[64]; + snprintf(dlname, 64, "%s%s%s%s", dot? "./": "", MODPRE, name, MODPOST); + MODULE module=DLOPEN(dlname); + if(module){ + k_module_loaded_fn loaded_fn=get_module_loaded_fn(name, module); + k_module_tick_fn tick_fn =get_module_tick_fn( name, module); + k_module_event_fn event_fn =get_module_event_fn( name, module); + if(loaded_fn && event_fn){ + add_module_entry(name, tick_fn, event_fn); + (*loaded_fn)(); + } + else k_log_err("Couldn't load module %s (hooks)", dlname); + } + else k_log_err("Couldn't load module %s (dlopen) %s", dlname, DLERROR); +} + +k_module_loaded_fn get_module_loaded_fn(char* name, MODULE module) +{ + snprintf(tmpbuf, TMPBUFSIZE, "%s%s", name, NAME_OF_MODULE_LOADED_FN); + k_module_loaded_fn f=(k_module_loaded_fn)DLSYM(module, tmpbuf); + if(!f) k_log_err("no %s", tmpbuf); + return f; +} + +k_module_tick_fn get_module_tick_fn(char* name, MODULE module) +{ + snprintf(tmpbuf, TMPBUFSIZE, "%s%s", name, NAME_OF_MODULE_TICK_FN); + k_module_tick_fn f=(k_module_tick_fn)DLSYM(module, tmpbuf); + if(!f) k_log_err("no %s", tmpbuf); + return f; +} + +k_module_event_fn get_module_event_fn(char* name, MODULE module) +{ + snprintf(tmpbuf, TMPBUFSIZE, "%s%s", name, NAME_OF_MODULE_EVENT_FN); + k_module_event_fn f=(k_module_event_fn)DLSYM(module, tmpbuf); + if(!f) k_log_err("no %s", tmpbuf); + return f; +} + +/* -------------------------------------------------------------------------- */ + +void add_module_entry(char* name, + k_module_tick_fn tick_fn, + k_module_event_fn event_fn) +{ + if(nextmodule==MAX_MODULES){ + k_fatal("Too many modules! Maximum %d", MAX_MODULES); + } + modules[nextmodule ].name =name; + modules[nextmodule ].tick_fn =tick_fn; + modules[nextmodule ].event_fn=event_fn; + modules[nextmodule++].queue =0; +} + +event* event_new(void* data) +{ + event* ev; + ev=k_malloc(sizeof(event)); + ev->next=0; + ev->data=data; + return ev; +} + +EXPORT int k_event_post(char* module, void* data) +{ + event* ev=event_new(data); + event** queue=get_module_queue(module); + if(!queue){ + k_log_err("k_event_post(%s): no such module", module); + return 0; + } + if(!*queue) *queue=ev; + else{ + event* p=*queue; + do{ + if(!p->next){ + p->next=ev; + break; + } + p=p->next; + } while(1); + } + return 1; +} + +event** get_module_queue(char* name) +{ + int i; + for(i=0; i< nextmodule; i++){ + if(!strcmp(modules[i].name, name)) return &modules[i].queue; + } + return 0; +} + +int run_queues(void) +{ + int i,j; + for(i=0; i< nextmodule; i++){ + for(j=0; modules[i].queue && j < MAX_EVENTS_PER_MODULE; j++){ + event* event=modules[i].queue; + modules[i].queue=event->next; + event->next=0; + (*modules[i].event_fn)(event->data); + k_free(event); + } + } + for(i=0; i< nextmodule; i++){ + if(modules[i].queue) return 1; + } + return 0; +} + +/* -------------------------------------------------------------------------- */ + +void init_keys(void) +{ + key_event* ke; + ke=k_malloc(sizeof(key_event)); + ke->key=0; + ke->down=0; + ke->next=0; + key_consumer=ke; + key_producer=ke; +} + +OTHER_THREAD void got_key(unsigned char key, int down) +{ + if(!key_producer) return; + key_event* ke; + ke=k_malloc(sizeof(key_event)); + ke->key=key; + ke->down=down; + ke->next=0; + key_producer->next=ke; + key_producer=ke; +} + +void next_keys(void) +{ + while(key_consumer!=key_producer){ + key_event* ke=key_consumer->next; + if(key_fn) (*key_fn)(ke->key, ke->down); + k_free(key_consumer); + key_consumer=ke; + } +} + +/* -}{----------------------------------------------------------------------- */ + +EXPORT char* k_channel_name(char* type, char* ip, int port, char* other) +{ + int ln=0; + int bufsize=TMPBUFSIZE; + + if(type) ln+=snprintf(tmpbuf+ln, bufsize-ln, "|%s|", type); + else ln+=snprintf(tmpbuf+ln, bufsize-ln, "|-|"); + if(ln>=bufsize) return 0; + + if(ip) ln+=snprintf(tmpbuf+ln, bufsize-ln, "%s|", ip); + else ln+=snprintf(tmpbuf+ln, bufsize-ln, "-|"); + if(ln>=bufsize) return 0; + + if(port) ln+=snprintf(tmpbuf+ln, bufsize-ln, "%d|", port); + else ln+=snprintf(tmpbuf+ln, bufsize-ln, "-|"); + if(ln>=bufsize) return 0; + + if(other) ln+=snprintf(tmpbuf+ln, bufsize-ln, "%s|", other); + else ln+=snprintf(tmpbuf+ln, bufsize-ln, "-|"); + if(ln>=bufsize) return 0; + + return k_strdup(tmpbuf); +} + +EXPORT k_channel* k_channel_connect_name(char* chanm, + k_channel_event rdcallback, + k_channel_event wrcallback) +{ + if(k_hashtable_get(connecting_chans, chanm)){ + if(1) k_log_out("Already connecting to %s", chanm); + return 0; + } + k_hashtable_set(connecting_chans, chanm, chanm); + + k_channel* chan=0; + char* t=k_strdup(chanm); + char* a=strchr(t+1, '|'); *a++=0; + char* p=strchr(a, '|'); *p++=0; + char* o=strchr(p, '|'); *o++=0; + int pi=atoi(p); + + if(strstr(t, "-client")){ + chan=k_channel_connect(t, a, pi, rdcallback, wrcallback, 0); + if(!chan) k_log_err("couldn't connect to %s:%d", a, pi); + } + else + if(strstr(t, "-server") && *a=='-'){ + chan=k_channel_listen(t, pi, 0, rdcallback, wrcallback, 0); + if(!chan) k_log_err("couldn't listen on port %d", pi); + } + else + if(strstr(t, "-server")){ + chan=k_channel_connect(t, a, pi, rdcallback, wrcallback, 0); + if(!chan) k_log_err("couldn't connect to %s:%d", a, pi); + } + + *--a= '|'; + *--p= '|'; + *--o= '|'; + + if(!chan) k_free(t); + + return chan; +} + +EXPORT k_channel* k_channel_get_name(char* chanm) +{ + k_channel* chan=k_hashtable_get(current_chans, chanm); + return chan; +} + +EXPORT void k_channel_show_all() +{ + k_log_out("----------------------------------------"); + k_hashtable_show(connecting_chans); + k_log_out("----------------------------------------"); + k_hashtable_show(current_chans); + k_log_out("----------------------------------------"); +} + +void gen_conn_name(k_channel* chan) +{ + char* t=chan->name+1; + char* a=strchr(t, '|'); *a++=0; + char* p=strchr(a, '|'); *p++=0; + char* o=strchr(p, '|'); *o++=0; + char* ip=inet_ntoa(chan->clientip); + int pi=atoi(p); + + char b[32]; + static long uniquenum; + snprintf(b, 32, "%ld", ++uniquenum); + chan->name=k_channel_name(t, ip, pi, b); + + *--a= '|'; + *--p= '|'; + *--o= '|'; +} + +/* -------------------------------------------------------------------------- */ + +EXPORT k_channel* k_channel_listen(char* name, + int listenport, + char* banner, + k_channel_event rdcallback, + k_channel_event wrcallback, + void* context) +{ + SOCK_T s=make_listen_socket(listenport); + if(!s) return 0; + + struct in_addr noclientip=EMPTY_IN_ADDR; + k_channel* chan=register_k_channel(name, + 0, + listenport, + banner, + rdcallback, + wrcallback, + context, + CHAN_LISTEN, + noclientip, + s); + int r=listen_socket(s, listenport); + if(r== -1){ + unregister_k_channel(chan,1); + return 0; + } + return chan; +} + +EXPORT k_channel* k_channel_connect(char* name, + char* listenhost, + int listenport, + k_channel_event rdcallback, + k_channel_event wrcallback, + void* context) +{ + SOCK_T s=make_connect_socket(); + if(!s) return 0; + + struct in_addr noclientip=EMPTY_IN_ADDR; + k_channel* chan=register_k_channel(name, + listenhost, + listenport, + 0, + rdcallback, + wrcallback, + context, + CHAN_ESTABL, + noclientip, + s); + int r=connect_socket(s, listenhost, listenport); + if(r== -1){ + unregister_k_channel(chan,1); + return 0; + } + return chan; +} + +EXPORT k_channel* k_channel_send(k_channel* chan, + char* base, + size_t size, + int free) +{ + append_to_wrbuffers(chan, base, size, free); + set_callback(chan, SETCB_WR); + return chan; +} + +EXPORT char* k_channel_chop_div(k_channel* chan, char* divider) +{ + return chop_from_rdbuffer_div(chan, divider); +} + +EXPORT char* k_channel_chop_len(k_channel* chan, size_t size) +{ + return chop_from_rdbuffer_len(chan, size); +} + +EXPORT int k_channel_setbuf(k_channel* chan, char* rdbuffer, size_t size) +{ + return set_rdbuffer(chan, rdbuffer, size); +} + +EXPORT char* k_channel_getbuf(k_channel* chan) +{ + return get_rdbuffer(chan); +} + +EXPORT void k_channel_close(k_channel* chan) +{ + unregister_k_channel(chan,0); +} + +k_channel* register_k_channel(char* name, + char* listenhost, + int listenport, + char* banner, + k_channel_event rdcallback, + k_channel_event wrcallback, + void* context, + int type, + struct in_addr clientip, + SOCK_T s) +{ + k_channel* chan; + chan=k_malloc(sizeof(k_channel)); + + chan->name=name; + chan->listenhost=listenhost; + chan->listenport=listenport; + chan->banner=banner; + chan->rdcallback=rdcallback; + chan->wrcallback=wrcallback; + chan->context=context; + chan->type=type; + chan->clientip=clientip; + chan->linger=0; + + chan->priv=k_malloc(sizeof(k_channel_priv)); + chan->priv->SOCK=s; + chan->priv->state=CHAN_OPEN; + chan->priv->event=0; /* what is this for? */ + chan->priv->rwmask=0; /* what is this for? */ + chan->priv->rdbuffer=(char*)k_malloc(RDSIZE); /* 40K per connection??!! */ + chan->priv->rdbufpos=0; + chan->priv->rdbufsize=RDSIZE; + chan->priv->rdbufforeign=0; + chan->priv->wrbuffers=(wrbuffer*)k_malloc(WRSIZE*sizeof(wrbuffer)); + chan->priv->wrbufpos=0; + chan->priv->wrbufsize=WRSIZE; + chan->priv->lingercount=LINGER_LOOPS; + + if(1){ + set_callback(chan, SETCB_RD); + } + if(chan->listenhost){ + set_callback(chan, SETCB_WR); + } + + chan->next=k_channels; + k_channels=chan; + + return chan; +} + +void unregister_k_channel(k_channel* chan, int now) +{ + int closing=CHAN_CLOSING(chan); + if(closing && !now) return; + int linger=chan->linger && !now; + chan->priv->state=linger? CHAN_LINGER: CHAN_CLOSE; + if(linger) un_set_callback(chan, SETCB_WR); + else un_set_callback(chan, SETCB_WR | SETCB_RD); + if(closing) return; + chan_closed(chan); + if(chan->rdcallback)(*chan->rdcallback)(chan, chan->priv->rdbufpos, -1); + else + if(chan->wrcallback)(*chan->wrcallback)(chan, chan->priv->wrbufpos, -1); +} + +void chan_closed(k_channel* chan) +{ + if(0) k_log_out("chan_closed %s", chan->name); + k_hashtable_remove(connecting_chans, chan->name); + k_hashtable_remove(current_chans, chan->name); + if(0) k_channel_show_all(); +} + +void do_regular_things(void) +{ + tick_modules(); + remove_dead_k_channels(); +} + +void tick_modules(void) +{ + int i; + for(i=0; i< nextmodule; i++){ + k_module_tick_fn tf=modules[i].tick_fn; + if(tf) (*tf)(); + } +} + +void remove_dead_k_channels(void) +{ + int i; + k_channel* cn; + k_channel* cp=0; + k_channel* cc=k_channels; + while(cc){ + cn=cc->next; + if( cc->priv->state==CHAN_CLOSE || + (cc->priv->state==CHAN_LINGER && !cc->priv->lingercount--)){ + un_set_callback(cc, SETCB_WR | SETCB_RD); + if(!cp) k_channels=cn; + else cp->next =cn; + SOCKET_CLOSE(cc->priv->SOCK); + for(i=0; i< cc->priv->wrbufpos; i++){ + k_free(cc->priv->wrbuffers[i].free); + } + k_free(cc->priv->wrbuffers); + k_free(cc->priv->rdbuffer); + k_free(cc->priv); + k_free(cc->name); + k_free(cc); + } + else cp=cc; + cc=cn; + } +} + +k_channel* find_k_channel_for_socket(SOCK_T s) +{ + k_channel* chan; + for(chan=k_channels; chan; chan=chan->next){ + if(chan->priv->SOCK==s) break; + } + return chan; +} + +void show_open_channels(void) +{ + k_channel* chan=k_channels; + while(chan){ + if(chan->priv->state==CHAN_OPEN ){ + if(0) k_log_out("chan open %x", chan->priv->SOCK); + } + if(chan->priv->state==CHAN_LINGER){ + if(0) k_log_out("chan lingering %x", chan->priv->SOCK); + } + if(chan->priv->state==CHAN_CLOSE ){ + if(0) k_log_out("chan closed %x", chan->priv->SOCK); + } + chan=chan->next; + } +} + +void readable_socket(k_channel* chan) +{ + if(chan->type==CHAN_LISTEN){ + register_connections(chan); + } + else{ + read_more_and_notify(chan); + } +} + +void writeable_socket(k_channel* chan) +{ + write_more_and_notify(chan); +} + +void exception_socket(k_channel* chan) +{ + if(0) k_log_out("exception_socket"); + unregister_k_channel(chan,1); +} + +void register_connections(k_channel* chan) +{ + SOCK_T as=chan->priv->SOCK; + struct sockaddr_in iaddr; + struct sockaddr* iaddrp=(struct sockaddr*)&iaddr; + socklen_t size=sizeof(struct sockaddr_in); + while(1){ + SOCK_T s; + int r=ACCEPT(as, iaddrp, &size, s); + GETERRNO(r); + if(r== -1){ + if(ERRNO==INTERRUPTED) break; + if(ISNOTACTIVE(ERRNO)) break; + log_net_err("accept", ERRNO); + break; + } + SET_NON_BLOCKING(s); + SET_NO_DELAY(s); + + if(chan->banner){ + SOCKET_WRITE(s, chan->banner, strlen(chan->banner)); + } + + k_channel* chann=register_k_channel(chan->name, + 0, + chan->listenport, + chan->banner, + chan->rdcallback, + chan->wrcallback, + chan->context, + CHAN_ESTABL, + iaddr.sin_addr, + s); + gen_conn_name(chann); + k_hashtable_set(current_chans, chann->name, chann); + if(chann->rdcallback) (*chann->rdcallback)(chann, 0, 0); + } +} + +void read_more_and_notify(k_channel* chan) +{ + int pos=chan->priv->rdbufpos; + int size=SOCKET_READ(chan->priv->SOCK, + chan->priv->rdbuffer +pos, + chan->priv->rdbufsize-pos); + GETERRNO(size); + if(size==0){ size= -1; ERRNO=EPIPE; } + if(size== -1){ + if(ERRNO==INTERRUPTED) return; + if(ISNOTACTIVE(ERRNO)) return; + log_net_err("socket_read", ERRNO); + unregister_k_channel(chan,1); + return; + } + if(CHAN_CLOSING(chan)) return; + + chan->priv->rdbufpos+=size; + + if(chan->rdcallback){ + (*chan->rdcallback)(chan, chan->priv->rdbufpos, size); + } + + if(chan->priv->rdbufpos==chan->priv->rdbufsize){ + if(chan->priv->rdbufforeign){ + k_log_err("Run out of room in given buffer"); + k_log_err("rdbufsize=%d", chan->priv->rdbufsize); + unregister_k_channel(chan,1); + return; + } + chan->priv->rdbufsize*=2; + chan->priv->rdbuffer=k_realloc(chan->priv->rdbuffer, + chan->priv->rdbufsize); + if(0) k_log_out("rdbufsize=%d", chan->priv->rdbufsize); + // DoS vulnerability unless buffer size limited + } +} + +void write_more_and_notify(k_channel* chan) +{ + if(!chan->priv->wrbufpos){ + if(chan->name){ + k_hashtable_remove(connecting_chans, chan->name); + k_hashtable_set( current_chans, chan->name, chan); + } + if(chan->wrcallback) (*chan->wrcallback)(chan, 0, 0); + if(!chan->priv->wrbufpos) un_set_callback(chan, SETCB_WR); // Eerrk? + return; + } + + int writemax=MIN(WRITEMAX, chan->priv->wrbuffers[0].size); + int size=SOCKET_WRITE(chan->priv->SOCK, + chan->priv->wrbuffers[0].base, + writemax); + GETERRNO(size); + if(size==0){ size= -1; ERRNO=NOTACTIVE; } + if(size== -1){ + if(ERRNO==INTERRUPTED) return; + if(ISNOTACTIVE(ERRNO)) return; + log_net_err("socket_write", ERRNO); + unregister_k_channel(chan,1); + return; + } + + chop_from_wrbuffers(chan, size); + + if(chan->wrcallback){ + (*chan->wrcallback)(chan, chan->priv->wrbufpos, size); + } + + if(!chan->priv->wrbufpos) un_set_callback(chan, SETCB_WR); +} + +char* strnstr(char* haystack, size_t size, char* needle) // Errk! +{ + if(!needle || !*needle) return haystack; + char* h; + char* e=haystack+size; + for(h=haystack; h < e && *h; h++){ + char* i=h; + char* n=needle; + while(i < e && *i && *n && *i==*n){ i++; n++; } + if(!*n) return h; + } + return 0; +} + +char* chop_from_rdbuffer_div(k_channel* chan, char* divider) +{ + char* s=strnstr(chan->priv->rdbuffer, chan->priv->rdbufpos, divider); + if(!s) return 0; + *s=0; + size_t size=(s+1 - chan->priv->rdbuffer); + s+=strlen(divider); + char* chunk=k_memdup(chan->priv->rdbuffer, size); + char* e=chan->priv->rdbuffer+chan->priv->rdbufpos; + memmove(chan->priv->rdbuffer, s, e-s); + chan->priv->rdbufpos=e-s; + return chunk; +} + +char* chop_from_rdbuffer_len(k_channel* chan, size_t size) +{ + if(!size || size > chan->priv->rdbufpos) return 0; + char* chunk=k_memdup(chan->priv->rdbuffer, size); + char* s=chan->priv->rdbuffer+size; + char* e=chan->priv->rdbuffer+chan->priv->rdbufpos; + memmove(chan->priv->rdbuffer, s, e-s); + chan->priv->rdbufpos=e-s; + return chunk; +} + +int set_rdbuffer(k_channel* chan, char* rdbuffer, size_t size) +{ + if(chan->priv->rdbufforeign) return BUFFER_ALREADY_SET; + size_t pos=chan->priv->rdbufpos; + if(pos>=size){ + memcpy(rdbuffer, chan->priv->rdbuffer, size); + char* s=chan->priv->rdbuffer+size; + char* e=chan->priv->rdbuffer+pos; + memmove(chan->priv->rdbuffer, s, e-s); + chan->priv->rdbufpos=e-s; + return BUFFER_FILLED; + } + memcpy(rdbuffer, chan->priv->rdbuffer, pos); + k_free(chan->priv->rdbuffer); + chan->priv->rdbuffer=rdbuffer; + chan->priv->rdbufsize=size; + chan->priv->rdbufforeign=1; + return BUFFER_SET; +} + +char* get_rdbuffer(k_channel* chan) +{ + if(!chan->priv->rdbufforeign) return 0; + char* rdbuffer=chan->priv->rdbuffer; + chan->priv->rdbuffer=(char*)k_malloc(RDSIZE); + chan->priv->rdbufpos=0; + chan->priv->rdbufsize=RDSIZE; + chan->priv->rdbufforeign=0; + return rdbuffer; +} + +void append_to_wrbuffers(k_channel* chan, char* base, size_t size, int free) +{ + if(chan->priv->wrbufpos==chan->priv->wrbufsize){ + chan->priv->wrbufsize*=2; + chan->priv->wrbuffers=k_realloc(chan->priv->wrbuffers, + chan->priv->wrbufsize * + sizeof(wrbuffer)); + if(0) k_log_out("wrbufsize=%d", chan->priv->wrbufsize); + } + chan->priv->wrbuffers[chan->priv->wrbufpos].base=base; + chan->priv->wrbuffers[chan->priv->wrbufpos].size=size; + chan->priv->wrbuffers[chan->priv->wrbufpos].free=free? base: 0; + chan->priv->wrbufpos++; +} + +void chop_from_wrbuffers(k_channel* chan, size_t size) +{ + if(chan->priv->wrbuffers[0].size != size){ + chan->priv->wrbuffers[0].base+=size; + chan->priv->wrbuffers[0].size-=size; + } + else{ + k_free(chan->priv->wrbuffers[0].free); + chan->priv->wrbufpos--; + int i; + for(i=0; i< chan->priv->wrbufpos; i++){ + chan->priv->wrbuffers[i]=chan->priv->wrbuffers[i+1]; + } + } +} + +/* -------------------------------------------------------------------------- */ + +SOCK_T make_listen_socket(int listenport) +{ + SOCK_T s; + int r=SOCKET(AF_INET, SOCK_STREAM, IPPROTO_TCP, s); + GETERRNO(r); + if(r== -1){ + log_net_err("socket", ERRNO); + return 0; + } + SET_NON_BLOCKING(s); + SET_REUSEADDR(s); + + struct sockaddr_in iaddr; + struct sockaddr* iaddrp=(struct sockaddr*)&iaddr; + iaddr=make_sockaddr_in(INADDR_ANY, listenport); + + r=BIND(s, iaddrp, sizeof(iaddr)); + GETERRNO(r); + if(r== -1){ + log_net_err("bind", ERRNO); + SOCKET_CLOSE(s); + return 0; + } + + return s; +} + +SOCK_T make_connect_socket(void) +{ + SOCK_T s; + int r=SOCKET(AF_INET, SOCK_STREAM, IPPROTO_TCP, s); + GETERRNO(r); + if(r== -1){ + log_net_err("socket", ERRNO); + return 0; + } + SET_NON_BLOCKING(s); + SET_NO_DELAY(s); + return s; +} + +int listen_socket(SOCK_T s, int listenport) +{ + if(0) k_log_out("Listening on port %d", listenport); + int r=LISTEN(s, 128); + GETERRNO(r); + if(r== -1){ + log_net_err("listen", ERRNO); + SOCKET_CLOSE(s); + return -1; + } + return 0; +} + +int connect_socket(SOCK_T s, char* listenhost, int listenport) +{ + if(0) k_log_out("Hanging on gethostbyname for %s", listenhost); + struct hostent* hostaddr=gethostbyname(listenhost); + if(!hostaddr){ + SOCKET_CLOSE(s); + return -1; + } + + struct sockaddr_in iaddr; + struct sockaddr* iaddrp=(struct sockaddr*)&iaddr; + iaddr=make_sockaddr_in(0, listenport); + memcpy(&iaddr.sin_addr, hostaddr->h_addr, hostaddr->h_length); + + if(0) k_log_out("Connect: %s(%s):%d ", + listenhost, inet_ntoa(iaddr.sin_addr), listenport); + do{ + int r=CONNECT(s, iaddrp, sizeof(iaddr)); + GETERRNO(r); + if(r== -1 && ERRNO==INTERRUPTED) continue; + if(r== -1 && !ISCONNECTING(ERRNO)){ + log_net_err("connect", ERRNO); + SOCKET_CLOSE(s); + return -1; + } + break; + }while(1); + + return 0; +} + +SOCK_T make_udp_listen_socket(int listenport) +{ + SOCK_T s; + int r; + struct sockaddr_in iaddr; + struct sockaddr* iaddrp=(struct sockaddr*)&iaddr; + + r=SOCKET(AF_INET, SOCK_DGRAM, IPPROTO_UDP, s); + GETERRNO(r); + if(r== -1){ + log_net_err("socket", ERRNO); + return 0; + } + + SET_REUSEADDR(s); + + iaddr=make_sockaddr_in(INADDR_ANY, listenport); + + r=BIND(s, iaddrp, sizeof(iaddr)); + GETERRNO(r); + if(r== -1){ + log_net_err("bind", ERRNO); + SOCKET_CLOSE(s); + return 0; + } + + return s; +} + +EXPORT SOCK_T make_udp_send_socket(char* listenhost, int listenport) +{ + SOCK_T s; + int r; + struct sockaddr_in iaddr; + struct sockaddr* iaddrp=(struct sockaddr*)&iaddr; + + r=SOCKET(AF_INET, SOCK_DGRAM, IPPROTO_UDP, s); + GETERRNO(r); + if(r== -1){ + log_net_err("socket", ERRNO); + return 0; + } + + struct hostent* hostaddr=gethostbyname(listenhost); + if(!hostaddr){ + log_net_err("gethostbyname", h_errno); + SOCKET_CLOSE(s); + return 0; + } + + iaddr=make_sockaddr_in(0, listenport); + memcpy(&iaddr.sin_addr, hostaddr->h_addr, hostaddr->h_length); + + r=CONNECT(s, iaddrp, sizeof(iaddr)); + GETERRNO(r); + if(r== -1){ + log_net_err("connect", ERRNO); + SOCKET_CLOSE(s); + return 0; + } + + return s; +} + +struct sockaddr_in make_sockaddr_in(unsigned long address, unsigned int port) +{ + struct sockaddr_in iaddr; + struct sockaddr* iaddrp=(struct sockaddr*)&iaddr; + memset(iaddrp, 0, sizeof(iaddr)); + iaddr.sin_addr.s_addr=htonl(address); + iaddr.sin_port =htons(port); + iaddrp->sa_family=AF_INET; + return iaddr; +} + +void log_net_err(char* where, int e) +{ + if(0) k_log_out("Network (%s): %s (%d)", where, str_error(e), e); +} + +/* -------------------------------------------------------------------------- */ + +EXPORT void k_file_stat(char* basedir, + char* filename, + k_file_event callback, + void* context) +{ + snprintf(tmpbuf, TMPBUFSIZE, "%s/%s", basedir, filename); + char* fullname=tmpbuf; + + k_stat kstat={0,0,0,0}; + + stat_only(fullname, &kstat); + (*callback)(basedir, filename, 0, 0, kstat, context); +} + +EXPORT void k_file_read(char* basedir, + char* filename, + size_t mmapsize, + size_t size, + k_file_event callback, + void* context) +{ + snprintf(tmpbuf, TMPBUFSIZE, "%s/%s", basedir, filename); + char* fullname=tmpbuf; + + FILE_T filehandle; + k_stat kstat={0,0,0,0}; + + int write=!!size; + if(write && !ensure_dir(fullname)){ + (*callback)(basedir, filename, 0, 0, kstat, context); + return; + } + + stat_only(fullname, &kstat); + if(!kstat.type && !write){ + (*callback)(basedir, filename, 0, 0, kstat, context); + return; + } + filehandle=open_only(fullname, write); + if(!filehandle){ + (*callback)(basedir, filename, 0, 0, kstat, context); + return; + } + if(!kstat.type || (write && kstat.size!=size)){ + if(ftruncate(filehandle, size)){ + FCLOSE(filehandle); + (*callback)(basedir, filename, 0, 0, kstat, context); + return; + } + } + FCLOSE(filehandle); + + if(!write) size=kstat.size; + else kstat.size=size; + + if(size>MAX_FILESIZE){ + (*callback)(basedir, filename, 0, 0, kstat, context); + return; + } + int dommap=(size >= mmapsize); + + int prot=(write? PROT_WRITE: 0)|PROT_READ; + + char* data; + + if(size==0) data=dommap? dummy_empty_data: k_malloc(1); + else + if(dommap) data=mmap_name( 0, size, prot, MAP_SHARED, fullname, 0); + else data=mmap_malloc(0, size, prot, MAP_SHARED, fullname, 0); + + if(data==MAP_FAILED){ + (*callback)(basedir, filename, 0, 0, kstat, context); + return; + } + (*callback)(basedir, filename, data, dommap, kstat, context); +} + +EXPORT int k_file_sync(char* data, size_t size) +{ + int r=msync(data, size, MS_ASYNC); + return !r; +} + +EXPORT void k_file_write(char* basedir, + char* filename, + char* data, + size_t datalength, + k_file_event callback, + void* context) +{ + snprintf(tmpbuf, TMPBUFSIZE, "%s/%s", basedir, filename); + char* fullname=tmpbuf; + + FILE_T filehandle; + k_stat kstat={0,0,0,0}; + + if(!ensure_dir(fullname)){ + (*callback)(basedir, filename, 0, 0, kstat, context); + return; + } + + filehandle=open_only(fullname, 1); + if(!filehandle){ + (*callback)(basedir, filename, 0, 0, kstat, context); + return; + } + if(datalength!=0) kstat.size=WRITEFILE(filehandle, data, datalength); + if(kstat.size== -1){ + FCLOSE(filehandle); + (*callback)(basedir, filename, 0, 0, kstat, context); + return; + } + FCLOSE(filehandle); + (*callback)(basedir, filename, data, 0, kstat, context); +} + +EXPORT void k_dir_list(char* basedir, + char* dirname, + char* pattern[], + char* format[], + k_file_event callback, + void* context) +{ + snprintf(tmpbuf, TMPBUFSIZE, "%s/%s", basedir, dirname); + char* fullname=k_strdup(tmpbuf); + + k_stat kstat={ STAT_D, 0, 0, 0 }; + char* buf=tmpbuf; + int size=TMPBUFSIZE; + int i; + for(i=0; format[i]; i++){ + int n=dir_list(buf, size, fullname, pattern[i], format[i]); + if(n== -1){ + (*callback)(basedir, dirname, 0, 0, kstat, context); + k_free(fullname); + return; + } + buf+=n; size-=n; + } + kstat.size=TMPBUFSIZE-size; + (*callback)(basedir, dirname, k_strdup(tmpbuf), 0, kstat, context); + k_free(fullname); +} + +int dir_list(char* buf, int size, char* fullname, char* pattern, char* format) +{ + DIR* d=opendir(fullname); + if(!d) return -1; + + char* b=buf; + int s=size; + *b=0; + struct dirent* de; + while((de=readdir(d))){ + + char* name=de->d_name; + if(*name=='.') continue; + + if(pattern && *pattern && + !k_string_matches_pattern(name, pattern)) continue; + + int n=snprintf(b, s, format, name, name, name, name, name); + if(n< 0 || n>=s){ closedir(d); return -1; } + b+=n; s-=n; + } + closedir(d); + return size-s; +} + +int ensure_dir(char* fullname) +{ + char* e=strrchr(fullname, '/'); + if(!e) return 1; + *e=0; + k_stat kstat={0,0,0,0}; + stat_only(fullname, &kstat); + if(kstat.type!=STAT_D){ + MKDIR(fullname); + stat_only(fullname, &kstat); + } + *e='/'; + return kstat.type==STAT_D; +} + +/* -------------------------------------------------------------------------- */ + +EXPORT int k_string_matches_pattern(char* string, char* pattern) +{ + char patt[32]; + strncpy(patt, pattern, 32); + patt[31]=0; + pattern=patt; + if(*pattern=='{'){ + while(1){ + pattern++; + char* e=strpbrk(pattern, ",}"); + if(!e) return 0; + char x=*e; *e=0; + if(k_string_matches_pattern(string, pattern)) return 1; + *e=x; + pattern=e; + } + } + else + if(*pattern=='*'){ + pattern++; + return k_string_ends_with(string, pattern); + } + return 0; +} + +EXPORT int k_string_ends_with(char* string, char* postfix) +{ + char* i=string+strlen(string)-strlen(postfix); + if(i= '0' && c <= '9')? c-'0': + (c >= 'A' && c <= 'F')? c-'A'+10: + (c >= 'a' && c <= 'f')? c-'a'+10: 0; +} + +/* -------------------------------------------------------------------------- */ + +EXPORT void k_log_out(char* format, ...) +{ + va_list ap; + va_start(ap, format); + if(LOG_TO_STD){ + VPRINTFOUT(format,ap); + PRINTFOUT("\n"); + FFLUSH(stdout); + } + else{ + vsnprintf(logbuf, LOGBUFSIZE,format,ap); + *(logbuf+LOGBUFSIZE-2)='\n'; + *(logbuf+LOGBUFSIZE-1)=0; + write_to_logfile_cern_style(logbuf,0); + } + va_end(ap); +} + +EXPORT void k_log_err(char* format, ...) +{ + va_list ap; + va_start(ap, format); + if(LOG_TO_STD){ + VPRINTFERR(format,ap); + PRINTFERR("\n"); + FFLUSH(stderr); + } + else{ + vsnprintf(logbuf, LOGBUFSIZE,format,ap); + *(logbuf+LOGBUFSIZE-2)='\n'; + *(logbuf+LOGBUFSIZE-1)=0; + write_to_logfile_cern_style(logbuf,1); + } + va_end(ap); +} + +EXPORT void k_fatal(char* format, ...) +{ + va_list ap; + va_start(ap, format); + k_log_out(format, ap); + va_end(ap); + k_log_out("Terminating Cilux"); + k_log_out("---------------------"); + EXIT(1); +} + +void write_to_logfile_cern_style(char* text, int error) +{ + if(!logfile) return; + + time_t now=time(0); + struct tm* tm; + int z; + if(log_gmt){ + tm=gmtime(&now); + z=0; + } + else{ + tm=localtime(&now); + z=TIMEZONE(tm)/60; + } + char sgn; + if(z >=0){ sgn='+'; } + else{ sgn='-'; z= -z; } + + char cern[64]; + char date[64]; + strftime(cern, sizeof(cern), CERNDATE, tm); +#ifdef SHOW_ZONE + snprintf(date, sizeof(date), "%s %c%02d%02d", cern, sgn, z/60, z%60); +#else + snprintf(date, sizeof(date), "%s", cern); +#endif + FPRINTF(logfile, "[%s|%s] %s%s\n", date, k_ciux, + error? "Warning: ": "", text); + FFLUSH(logfile); +} + +/* -------------------------------------------------------------------------- */ + +#define EXAMPLE_DATE Fri, 04 Feb 2005 12:14:48 GMT +#define RFC1123STRF "%a, %d %b %Y %H:%M:%S GMT" +#define RFC1123SSCANF "%3s, %d %3s %4d %2d:%2d:%2d GMT" + +static char b[100]; + +EXPORT char* k_time_to_rfc(time_t t) +{ + strftime(b, sizeof(b), RFC1123STRF, gmtime(&t)); + return b; +} + +EXPORT char* k_time_to_rfc_relative(int plus) +{ + time_t t=time(0)+plus; + strftime(b, sizeof(b), RFC1123STRF, gmtime(&t)); + return b; +} + +EXPORT time_t k_time_from_rfc(char* s) +{ + if(!s || strlen(s)!=29) return -1; + struct tm tm; tm.tm_wday=0; tm.tm_yday=0; tm.tm_isdst=0; + char wd[32], mn[32]; + int r=sscanf(s, RFC1123SSCANF, wd, &tm.tm_mday, mn, &tm.tm_year, + &tm.tm_hour, &tm.tm_min, &tm.tm_sec); + if(r!=7) return -1; + tm.tm_mon=(int)k_hashtable_get(monthhash, mn); + if(tm.tm_mon) tm.tm_mon -=1; + tm.tm_year-=1900; + return MKTIME(&tm); +} + +EXPORT void k_time_get_now(char* buf, size_t size, char* format) +{ + time_t now=time(0); + struct tm* tm; + int gmt=1; + if(gmt){ + tm=gmtime(&now); + } + else{ + tm=localtime(&now); + } + strftime(buf, size, format, tm); +} + +/* -------------------------------------------------------------------------- */ + +typedef struct hash_item hash_item; + +typedef struct hashtable{ struct hashtable* next; + char* name; + int buckets; + int size; + hash_item** lists; + int ignorecase; +} hashtable; + +struct hash_item{ hash_item* next; + char* key; + void* val; + int free; + k_hashtable* sub; +}; + +/* -------------------------------------------------------------------------- */ + +#define STRCMPCASE(tab, listkey, key)\ +((tab)->ignorecase? strcasecmp((listkey), (key)): strcmp((listkey), (key))) +#define BX 30 +#define WARN_SIZE(h)\ +if((h)->size && !((h)->size % BX)) k_log_out("%s# %d", (h)->name, (h)->size); + +EXPORT k_hashtable* k_hashtable_new(char* name, int ignorecase) +{ + hashtable* tab; + tab=k_malloc(sizeof(hashtable)); + tab->name=name; + tab->buckets=BX; + tab->size=0; + tab->lists=k_malloc((tab->buckets)*sizeof(hash_item*)); + tab->ignorecase=ignorecase; + tab->next=0; + int i; for(i=0; i< tab->buckets; i++) tab->lists[i]=0; + return (k_hashtable*)tab; +} + +k_hashtable* k_hashtable_dup2(k_hashtable* tab, int deep) +{ + if(!tab) return 0; + hashtable* tah=(hashtable*)tab; + hashtable* tad; + tad=k_malloc(sizeof(hashtable)); + tad->name=tah->name; + tad->buckets=tah->buckets; + tad->size=tah->size; + tad->lists=k_malloc((tah->buckets)*sizeof(hash_item*)); + tad->ignorecase=tah->ignorecase; + tad->next=0; + hash_item** lisp=0; + hash_item** lisd=0; + int i; + for(i=0; i< tah->buckets; i++){ + for(lisp=&tah->lists[i], lisd=&tad->lists[i]; + (*lisp); + lisp=&(*lisp)->next, lisd=&(*lisd)->next){ + + (*lisd)=k_malloc(sizeof(hash_item)); + (*lisd)->key=k_strdup((*lisp)->key); + (*lisd)->val=0; + (*lisd)->free=0; + (*lisd)->sub=0; + if((*lisp)->val){ + (*lisd)->val=k_strdup((*lisp)->val); + (*lisd)->free=1; + } + if(deep && (*lisp)->sub){ + (*lisd)->sub=k_hashtable_dup2((*lisp)->sub, 1); + } + if(!(*lisd)->val && !(*lisd)->sub){ + (*lisd)->val="..."; + } + } + (*lisd)=0; + } + tad->next=(hashtable*)k_hashtable_dup2(tab->next, deep); + return (k_hashtable*)tad; +} + +EXPORT k_hashtable* k_hashtable_dup(k_hashtable* tab) +{ + return k_hashtable_dup2(tab, 0); +} + +EXPORT k_hashtable* k_hashtable_deep(k_hashtable* tab) +{ + return k_hashtable_dup2(tab, 1); +} + +EXPORT k_hashtable* k_hashtable_dip(k_hashtable* tab, char* includes[]) +{ + if(!tab) return 0; + hashtable* tah=(hashtable*)tab; + hashtable* tad; + tad=(hashtable*)k_hashtable_new(tah->name, tah->ignorecase); + int i; + for(i=0; includes[i]; i++){ + char* in=k_hashtable_get((k_hashtable*)tah, includes[i]); + if(!in) continue; + in=k_strdup(in); + k_hashtable_put((k_hashtable*)tad, includes[i], in); + } + tad->next=(hashtable*)k_hashtable_dip(tab->next, includes); + return (k_hashtable*)tad; +} + +EXPORT void k_hashtable_merge(k_hashtable* tab, k_hashtable* tam) +{ + hashtable* tan=(hashtable*)tam; + hash_item** lisp; + int i; + for(i=0; i< tan->buckets; i++){ + for(lisp=&tan->lists[i]; (*lisp); lisp=&(*lisp)->next){ + char* key =(*lisp)->key; + void* val =(*lisp)->val; + int free=(*lisp)->free; + k_hashtable* sub =(*lisp)->sub; + if(!*key && free) continue; + if(val){ + k_hashtable_put(tab, key, k_strdup(val)); + } + if(sub){ + k_hashtable_sub(tab, key, k_hashtable_dup(sub)); + } + } + } +} + +void k_hashtable_set_put(k_hashtable* tab, char* key, void* val, int free) +{ + if(!(tab && key && val)) return; + hashtable* tah=(hashtable*)tab; + hash_item** lisp; + lisp=&tah->lists[string_hash(key) % tah->buckets]; + while((*lisp) && STRCMPCASE(tah, (*lisp)->key, key)){ + lisp=&(*lisp)->next; + } + if(!(*lisp)){ + (*lisp)=k_malloc(sizeof(hash_item)); + (*lisp)->key=k_strdup(key); + (*lisp)->val=val; + (*lisp)->free=free; + (*lisp)->sub=0; + (*lisp)->next=0; + tah->size++; + WARN_SIZE(tah); + } + else{ + if((*lisp)->free) k_free((*lisp)->val); + k_hashtable_delete((*lisp)->sub); + (*lisp)->val=val; + (*lisp)->free=free; + (*lisp)->sub=0; + } +} + +EXPORT void k_hashtable_set(k_hashtable* tab, char* key, void* val) +{ + k_hashtable_set_put(tab, key, val, 0); +} + +EXPORT void k_hashtable_put_int(k_hashtable* tab, char* key, int val) +{ + snprintf(tmpbuf, TMPBUFSIZE, "%d", val); + k_hashtable_set_put(tab, key, k_strdup(tmpbuf), 1); +} + +EXPORT void k_hashtable_put(k_hashtable* tab, char* key, void* val) +{ + k_hashtable_set_put(tab, key, val, 1); +} + +EXPORT void k_hashtable_put_dup(k_hashtable* tab, char* key, char* val) +{ + k_hashtable_set_put(tab, key, k_strdup(val), 1); +} + +EXPORT void k_hashtable_sub(k_hashtable* tab, char* key, k_hashtable* sub) +{ + if(!(tab && key && sub)) return; + sub->next=0; + hashtable* tah=(hashtable*)tab; + hash_item** lisp; + lisp=&tah->lists[string_hash(key) % tah->buckets]; + while((*lisp) && STRCMPCASE(tah, (*lisp)->key, key)){ + lisp=&(*lisp)->next; + } + if(!(*lisp)){ + (*lisp)=k_malloc(sizeof(hash_item)); + (*lisp)->key=k_strdup(key); + (*lisp)->val=0; + (*lisp)->free=0; + (*lisp)->sub=sub; + (*lisp)->next=0; + tah->size++; + WARN_SIZE(tah); + } + else{ + k_hashtable* val =(*lisp)->val; + k_hashtable* head=(*lisp)->sub; + if(!head){ + if(val) k_log_err("key '%s' in use: no sub!", key); + else (*lisp)->sub=sub; + } + else{ + while(head->next) head=head->next; + head->next=sub; + } + } +} + +EXPORT void* k_hashtable_get(k_hashtable* tab, char* key) +{ + if(!(tab && key)) return 0; + hashtable* tah=(hashtable*)tab; + hash_item* list; + list=tah->lists[string_hash(key) % tah->buckets]; + while(list && STRCMPCASE(tah, list->key, key)){ + list=list->next; + } + return list? (list->val? list->val: list->sub): 0; +} + +EXPORT int k_hashtable_get_int(k_hashtable* tab, char* key) +{ + char* val=k_hashtable_get(tab, key); + return val? atoi(val): 0; +} + +EXPORT char* k_hashtable_get_dup(k_hashtable* tab, char* key) +{ + char* v=k_hashtable_get(tab, key); + return k_strdup(v); +} + +EXPORT int k_hashtable_is(k_hashtable* tab, char* key, char* val) +{ + char* v=k_hashtable_get(tab, key); + return (v && val && !strcmp(v, val)); +} + +EXPORT int k_hashtable_isi(k_hashtable* tab, char* key, char* val) +{ + char* v=k_hashtable_get(tab, key); + return (v && val && !strcasecmp(v, val)); +} + +EXPORT int k_hashtable_isn(k_hashtable* tab, char* key, char* val, size_t size) +{ + char* v=k_hashtable_get(tab, key); + return (v && val && !strncmp(v, val, size)); +} + +EXPORT void* k_hashtable_extract(k_hashtable* tab, char* key) +{ + hashtable* tah=(hashtable*)tab; + hash_item** lisp; + hash_item* next; + lisp=&tah->lists[string_hash(key) % tah->buckets]; + while((*lisp) && STRCMPCASE(tah, (*lisp)->key, key)){ + lisp=&(*lisp)->next; + } + void* val=0; + if((*lisp)){ + next= (*lisp)->next; + k_free((*lisp)->key); + val= (*lisp)->val; + if(!val) val=(*lisp)->sub; + k_free((*lisp)); + (*lisp)=next; + tah->size--; + WARN_SIZE(tah); + } + return val; +} + +EXPORT void k_hashtable_remove(k_hashtable* tab, char* key) +{ + hashtable* tah=(hashtable*)tab; + hash_item** lisp; + hash_item* next; + lisp=&tah->lists[string_hash(key) % tah->buckets]; + while((*lisp) && STRCMPCASE(tah, (*lisp)->key, key)){ + lisp=&(*lisp)->next; + } + if((*lisp)){ + next= (*lisp)->next; + k_free((*lisp)->key); + if((*lisp)->free) k_free((*lisp)->val); + k_hashtable_delete((*lisp)->sub); + k_free((*lisp)); + (*lisp)=next; + tah->size--; + WARN_SIZE(tah); + } +} + +EXPORT void k_hashtable_delete(k_hashtable* tab) +{ + if(!tab) return; + hashtable* tah=(hashtable*)tab; + hash_item* list; + hash_item* next; + int i; + for(i=0; i< tah->buckets; i++){ + list=tah->lists[i]; + while(list){ + next=list->next; + k_free(list->key); + if(list->free) k_free(list->val); + k_hashtable_delete(list->sub); + k_free(list); + list=next; + } + } + k_free(tah->lists); + k_hashtable_delete((k_hashtable*)tah->next); + k_free(tah); +} + +EXPORT void k_hashtable_apply(k_hashtable* tab, + k_hashtable_apply_fn fn, + void* arg) +{ + if(!tab) return; + hashtable* tah=(hashtable*)tab; + hash_item** lisp; + int i; + for(i=0; i< tah->buckets; i++){ + for(lisp=&tah->lists[i]; (*lisp); lisp=&(*lisp)->next){ + if((*lisp)->val){ + (*fn)(arg, (*lisp)->key, (*lisp)->val); + } + else{ + (*fn)(arg, (*lisp)->key, (*lisp)->sub); + } + } + } +} + +void k_hashtable_show2(k_hashtable* tab, int chars) +{ + if(!tab) return; + hashtable* tah=(hashtable*)tab; + if(!chars) k_log_out("%s size=%d", tah->name, tah->size); + hash_item** lisp; + int i; + for(i=0; i< tah->buckets; i++){ + for(lisp=&tah->lists[i]; (*lisp); lisp=&(*lisp)->next){ + if(chars){ + if(*((*lisp)->key)){ + k_log_out("%s %s", + (*lisp)->key, (char*)((*lisp)->val)); + } + } + else{ + k_log_out("buck %d key '%s' val %x sub %x", + i, (*lisp)->key, (*lisp)->val, (*lisp)->sub); + } + } + } +} + +EXPORT void k_hashtable_show(k_hashtable* tab) +{ + k_hashtable_show2(tab,0); +} + +EXPORT void k_hashtable_show_chars(k_hashtable* tab) +{ + k_hashtable_show2(tab,1); +} + +int k_hashtable_snprintf_xi(k_hashtable* tab, + char* buf, + size_t size, + char* includes[], + char* excludes[], + int indent) +{ + if(!tab){ if(size) *buf=0; return 0; } + char ind[]=" "; + ind[indent]=0; + hashtable* tah=(hashtable*)tab; + *buf=0; + char* at=buf; + int ln=size; + hash_item** lisp; + int i; + for(i=0; i< tah->buckets; i++){ + for(lisp=&tah->lists[i]; (*lisp); lisp=&(*lisp)->next){ + if(!*((*lisp)->key)) continue; + if(check_i((*lisp)->key, includes, tah->ignorecase)) continue; + if(check_x((*lisp)->key, excludes, tah->ignorecase)) continue; + int n; + if((*lisp)->val){ + n=snprintf(at, ln, "%s%s %s" CRLF, ind, (*lisp)->key, + (char*)((*lisp)->val)); + if(n< 0) return n; + if(n>=ln) return size; + at+=n; ln-=n; + } + else{ + k_hashtable* sub=(*lisp)->sub; + do{ + n=snprintf(at, ln, "%s%s" CRLF, + ind, (*lisp)->key); + if(n< 0) return n; + if(n>=ln) return size; + at+=n; ln-=n; + + n=k_hashtable_snprintf_xi(sub, at, ln, 0, 0, + indent+8); + if(n< 0) return n; + if(n>=ln) return size; + at+=n; ln-=n; + + }while((sub=sub->next)); + } + } + } + return size-ln; +} + +EXPORT int k_hashtable_snprintf(k_hashtable* tab, char* buf, size_t size) +{ + return k_hashtable_snprintf_xi(tab, buf, size, 0, 0, 0); +} + +EXPORT int k_hashtable_snprintf_i(k_hashtable* tab, + char* buf, + size_t size, + char* includes[]) +{ + return k_hashtable_snprintf_xi(tab, buf, size, includes, 0, 0); +} + +EXPORT int k_hashtable_snprintf_x(k_hashtable* tab, + char* buf, + size_t size, + char* excludes[]) +{ + return k_hashtable_snprintf_xi(tab, buf, size, 0, excludes, 0); +} + +int check_i(char* key, char* includes[], int ignorecase) +{ + if(!includes) return 0; + int e; + for(e=0; includes[e]; e++){ + if(ignorecase? !strcasecmp(key, includes[e]): + !strcmp( key, includes[e]) ) break; + } + return !includes[e]; +} + +int check_x(char* key, char* excludes[], int ignorecase) +{ + if(!excludes) return 0; + int e; + for(e=0; excludes[e]; e++){ + if(ignorecase? !strcasecmp(key, excludes[e]): + !strcmp( key, excludes[e]) ) break; + } + return !!excludes[e]; +} + +unsigned int string_hash(char* p) +{ + unsigned int h=0; + while(*p) h=(h<<5)-h+tolower(*p++); + return h; +} + + +/* -------------------------------------------------------------------------- */ + + + diff --git a/src/platform/linux/cilux.c b/src/platform/linux/cilux.c new file mode 100644 index 0000000..009d621 --- /dev/null +++ b/src/platform/linux/cilux.c @@ -0,0 +1,73 @@ + +#include +#include +#include +#include + +#include +#include +#include "version.h" + +/* -------------------------------------------------------------------------- */ + +static void re_exec_as_other(); + +/* -------------------------------------------------------------------------- */ + +int main(int argc, char* argv[]) +{ + c_init(0, cilux_version, cilux_ciux, 0); + re_exec_as_other(); + c_run(0); + + return 0; +} + +void re_exec_as_other() +{ + char* other="other"; + uid_t uid=0; + gid_t gid=0; + if(getuid()==0){ + struct passwd* pw=getpwnam(other); + if(!pw){ printf("fail: getpwnam(\"%s\");\n", other); exit(1); } + uid=pw->pw_uid; + gid=pw->pw_gid; + } + + pid_t pid=fork(); + switch(pid){ + case 0: + break; + case -1: + printf("Failed to fork\n"); + exit(1); + default: + printf("pid %d\n", pid); + exit(0); + } + setsid(); + + if(getuid()==0){ + if(setgroups(0,0)== -1){ + printf("fail: setgroups(0,0)\n"); + exit(1); + } + if(setgid(gid)== -1){ + printf("fail: setgid(%d)\n", gid); + exit(1); + } + if(initgroups(other, gid)== -1){ + printf("fail: initgroups(\"%s\",%d)\n", other, gid); + exit(1); + } + if(setuid(uid)== -1){ + printf("fail: setuid(%d)\n", uid); + exit(1); + } + } +} + +/* -------------------------------------------------------------------------- */ + + diff --git a/src/platform/linux/osapi.c b/src/platform/linux/osapi.c new file mode 100644 index 0000000..96d64b7 --- /dev/null +++ b/src/platform/linux/osapi.c @@ -0,0 +1,275 @@ + +/* -------------------------------------------------------------------------- */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* -------------------------------------------------------------------------- */ + +static fd_set rd_fd_set; +static fd_set wr_fd_set; +static fd_set ex_fd_set; + +/* -------------------------------------------------------------------------- */ + +static void signal_terminate(int); +static void signal_alarm(int); +static void signal_other(int); +static void set_timeval_ms(struct timeval* t, int ms); + +/* -------------------------------------------------------------------------- */ + +void init_thread(void) +{ + signal(SIGTERM, signal_terminate); + signal(SIGINT, signal_terminate); + signal(SIGQUIT, signal_terminate); + signal(SIGALRM, signal_alarm); + signal(SIGPIPE, signal_other); + signal(SIGCHLD, SIG_IGN); + signal(SIGHUP, SIG_IGN); + signal(SIGUSR1, SIG_IGN); + signal(SIGUSR2, SIG_IGN); +} + +void init_gl(void) +{ +} + +void init_net(void) +{ + FD_ZERO(&rd_fd_set); + FD_ZERO(&wr_fd_set); + FD_ZERO(&ex_fd_set); +} + +/* -------------------------------------------------------------------------- */ + +OTHER_THREAD void signal_terminate(int signum) +{ + c_running(0); +} + +OTHER_THREAD void signal_alarm(int signum) +{ + c_signal(signum); +} + +OTHER_THREAD void signal_other(int signum) +{ + c_signal(signum); +} + +/* -------------------------------------------------------------------------- */ + +void set_callback(k_channel* chan, int rdwr) +{ + if(rdwr & SETCB_RD) FD_SET(chan->priv->SOCK, &rd_fd_set); + if(rdwr & SETCB_WR) FD_SET(chan->priv->SOCK, &wr_fd_set); +} + +void un_set_callback(k_channel* chan, int rdwr) +{ + if(rdwr & SETCB_RD) FD_CLR(chan->priv->SOCK, &rd_fd_set); + if(rdwr & SETCB_WR) FD_CLR(chan->priv->SOCK, &wr_fd_set); +} + +void poller(int no_block) +{ + fd_set rd=rd_fd_set; + fd_set wr=wr_fd_set; + fd_set ex=ex_fd_set; + + k_channel* chan; + + int highest=0; + for(chan=k_channels; chan; chan=chan->next){ + if(chan->priv->state==CHAN_CLOSE) continue; + if(highest < chan->priv->SOCK) highest=chan->priv->SOCK; + } + + struct timeval t; + struct timeval* tp=&t; + set_timeval_ms(tp, no_block? 0: LOOP_TICK); + + if(highest==0){ + select(0, 0, 0, 0, tp); + next_keys(); + if(!no_block) do_regular_things(); + return; + } + + int len=select(highest+1, &rd, &wr, &ex, tp); + GETERRNO(len); + + next_keys(); + + if(len==0){ + do_regular_things(); + return; + } + if(len== -1){ + if(ERRNO==INTERRUPTED) return; + log_net_err("select", ERRNO); + sleep(1); + do_regular_things(); + return; + } + + for(chan=k_channels; chan; chan=chan->next){ + if(FD_ISSET(chan->priv->SOCK, &ex)){ + exception_socket(chan); + continue; + } + if(FD_ISSET(chan->priv->SOCK, &wr)){ + int err; socklen_t len=sizeof(int); + if(getsockopt(chan->priv->SOCK, + SOL_SOCKET, + SO_ERROR, &err, &len) || err){ + exception_socket(chan); + continue; + } + else{ + writeable_socket(chan); + } + } + if(FD_ISSET(chan->priv->SOCK, &rd)){ + readable_socket(chan); + } + } +} + +void set_timeval_ms(struct timeval* t, int ms) +{ + t->tv_sec=ms/1000; + t->tv_usec=(ms-(t->tv_sec)*1000)*1000; +} + +char* str_error(int e) +{ + return strerror(e); +} + +/* -------------------------------------------------------------------------- */ + +void stat_only(char* fullname, k_stat* kstat) +{ + kstat->type=0; + struct stat s; + if(stat(fullname, &s)) return; + kstat->type=s.st_mode & 0170000; + kstat->size=s.st_size; + kstat->time=s.st_mtime; + kstat->perm=s.st_mode & 0007777; +} + +FILE_T stat_open(char* fullname, k_stat* kstat) +{ + stat_only(fullname, kstat); + if(!kstat->type) return 0; + FILE_T filehandle=open(fullname, O_RDONLY); + if(filehandle<0) return 0; + return filehandle; +} + +FILE_T open_only(char* fullname, int wr) +{ + int rw=wr? O_RDWR|O_CREAT|O_TRUNC: O_RDONLY; + FILE_T filehandle=open(fullname, rw, 0644); + if(filehandle<0) return 0; + return filehandle; +} + +void* mmap_malloc(void* s, size_t size, int prot, int f, char* fullname, int o) +{ + FILE_T fh=open(fullname, O_RDONLY, 0644); + if(fh<0) return MAP_FAILED; + + char* data=k_malloc(size); + int charsread=0; + int len; + do{ + len=read(fh, data+charsread, size-charsread); + if(len< 0 && FERRNO(len)==EINTR) continue; + if(len<=0) break; + charsread+=len; + } while(charsreadtm_gmtoff +#define SLEEP_MS(x) do{struct timeval t;t.tv_sec=0;t.tv_usec=(x)*1000;select(0,0,0,0,&t);}while(0) +#define MKTIME mktime + +/* -------------------------------------------------------------------------- */ + diff --git a/src/platform/linux/platform.h b/src/platform/linux/platform.h new file mode 100644 index 0000000..85c1e52 --- /dev/null +++ b/src/platform/linux/platform.h @@ -0,0 +1,27 @@ +#ifndef LINUX_PLATFORM_H +#define LINUX_PLATFORM_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MMAPSIZE (128*1024) + +#ifndef PUBLIC +#define PUBLIC extern +#endif +#define EXPORT + +#define CONSOLE void* +#define C_RUN_RV int +#define C_RUN_ARG void* + +#endif + -- 1.7.9.5