/**************************************************************** Copyright (C) 2012 AMPL Optimization, Inc.; written by David M. Gay. Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies and that both that the copyright notice and this permission notice and warranty disclaimer appear in supporting documentation. The author and AMPL Optimization, Inc. disclaim all warranties with regard to this software, including all implied warranties of merchantability and fitness. In no event shall the author be liable for any special, indirect or consequential damages or any damages whatsoever resulting from loss of use, data or profits, whether in an action of contract, negligence or other tortious action, arising out of or in connection with the use or performance of this software. ****************************************************************/ /* Proxy table handler */ #ifdef _WIN32 #include #include #include #include #define socklen_t int #define SLASH '\\' #else #include #include #include #include #include #include #define SLASH '/' #endif #include #include #include #include #include #include #include #ifdef STAND_ALONE /*{{*/ #include "asl.h" #include "avltree.h" #else /*}{*/ #ifndef _WIN32 #ifdef __hpux #define NO_SELECT_H #endif #ifndef NO_SELECT_H /* for hpux */ #include #endif #endif #endif /*}}*/ #include "funcadd.h" #include "arith.h" /* for Arith_Kind_ASL and X64_bit_pointers */ #include "tableproxyver.h" #ifdef X64_bit_pointers #define Bits "32" #define Bits1 "64" #else #define Bits "64" #define Bits1 "32" #endif /* tableproxy.h { */ #define TABLEPROXY_MAGIC "\nTable_Proxy \n" typedef unsigned int Uint; enum { TPH_n_Uint = 10 }; /* number of (adjacent) Uint fields after arith in a TP_Head */ typedef struct TP_Head { char magic[16]; Uint arith; Uint job; Uint TIflags; Uint arity; /* table arity */ Uint ncols; /* number of columns */ Uint nrcols; /* number of real-valued cols */ Uint nscols; /* number of string-valued cols */ /* Some columns may have both real and string values. */ Uint nstrings; /* number of strings in table declaration */ Uint nrows; /* For reading, a suggestion for the maximum number rows to return */ /* at once or the number of rows actually in this batch; */ /* for writing, the number of rows to write. */ Uint nrtab; /* number of values in the real-values table */ Uint nitab; /* number of values in the itable (type Uint) */ Uint tablen; /* total length of itable, rtable, and stable */ /* After the header come the real-values table (rtable), the */ /* itable (an array of Uint values), and the string table. The */ /* rtable has nrcols columns of length nrows, for a total of */ /* nrtab = nrcols * nrows elements. The itable has nitab elements */ /* and begins with ncols pairs (ir,is), with ir == 0 meaning no */ /* real data for this column ir > 0 meaning real-table column ir-1 */ /* is for this column; similarly, is == 0 means no string data for */ /* this column and is > 1 means column is-1 of the itable string */ /* columns is for this column. Then come nscols * nrows values for the */ /* itable string-column array. The string table starts with the table */ /* name, which is followed by nstrings values for the strings in the */ /* table declaration, followed by string values for the table. The */ /* elements of the string columns are indexed by elements of the itable */ /* string-column array, giving offsets in the string table, with offset */ /* 0 meaning "use the real value" and offset 1 meaning "use the Missing */ /* data value". If nitab > 2*ncols + nscols*nrows, then the itable */ /* element following the itable string-column array is the offset of */ /* in the string table of "verbose" output for this transaction. */ /* To return an error, we set the TPx_Error bit in job and return */ /* the error message as the one string value (the whole stable). */ } TP_Head; /* values for TP_Head.job */ enum { TPx_Read = 0, TPx_Write = 1, TPx_Done = 2, TPx_Error = 4, TPx_quit = 5, TPx_status = 6 }; /* } end of tableproxy.h */ typedef struct ProxProg ProxProg; typedef struct MemBlock MemBlock; typedef int (*ProxRW)(ProxProg*, void*, Uint); typedef const char cchar; struct ProxProg { ProxProg *prnext; char *name; /* program or IP addr */ char *hname; ProxRW read, write; #ifdef _WIN32 HANDLE hr, hw; #endif int fr, fw; int port; /* -1 for program */ int proto; int remote; int rowchunk; int verbose; }; struct MemBlock { union { MemBlock *next; real align; } u; }; typedef struct ProxyInfo { ProxProg *progs, *remote; char *mend, *mnext; MemBlock *curmb; int proto; int remotestarted; int verbose; } ProxyInfo; static ProxyInfo PI0; #ifndef STAND_ALONE /*{*/ static void* Malloc0(AmplExports *ae, size_t L) { void *rv = malloc(L); if (!rv) { fprintf(Stderr, "malloc(%lu) failure in tableproxy" Bits1 ".dll\n", (unsigned long)L); exit(1); } return rv; } static void* Malloc1a(AmplExports *ae, size_t L) { MemBlock *mb; size_t La; void *rv; La = PI0.mend - PI0.mnext; L = (L + sizeof(real) - 1) & ~(sizeof(real) - 1); if (La < L) { if (L >= 2048) { mb = (MemBlock*)Malloc0(ae, L + sizeof(MemBlock)); rv = (void*)(mb+1); } else { mb = (MemBlock*)Malloc0(ae, 4096 + sizeof(MemBlock)); rv = (void*)(mb+1); PI0.mnext = (char*)rv + L; PI0.mend = (char*)rv + 4096; } mb->u.next = PI0.curmb; PI0.curmb = mb; } else { rv = (void*)PI0.mnext; PI0.mnext += L; } return rv; } #endif /*}*/ typedef struct IRpair { Uint ir, is; } IRpair; #ifdef _WIN32 /*{{*/ static int wsa_started; static int wsa_startup(void) { WORD verreq; WSADATA wsaData; if (wsa_started) return 0; verreq = MAKEWORD(2,2); if (WSAStartup(verreq, &wsaData)) return 1; wsa_started = 1; return 0; } static int SocRead(ProxProg *p, void *v, Uint L) { char *s; int k, n, rv; s = (char*)v; k = (int)L; for(rv = 0;; s += n) { if ((n = recv(p->fr, s, k, 0)) <= 0) return -1; rv += n; if ((k -= n) <= 0) break; } return rv; } static int SocWrite(ProxProg *p, void *v, Uint L) { char *s; int k, n, rv; s = (char*)v; k = (int)L; for(rv = 0;; s += n) { if ((n = send(p->fw, s, k, 0)) <= 0) return -1; rv += n; if ((k -= n) <= 0) break; } return rv; } static int Read(ProxProg *p, void *v, Uint L) { DWORD k, n; char *s; int rv; s = (char*)v; k = L; for(rv = 0;; s += n) { if (!ReadFile(p->hr, s, k, &n, 0)) return -1; rv += n; if ((k -= n) <= 0) break; } return rv; } static int Write(ProxProg *p, void *v, Uint L) { DWORD k, n; char *s; int rv; s = (char*)v; k = L; for(rv = 0;; s += n) { if (!WriteFile(p->hw, s, k, &n, 0)) return -1; rv += n; if ((k -= n) <= 0) break; } return rv; } #else /*}{*/ static int Read(ProxProg *p, void *v, Uint L) { char *s; int k, n, rv; s = (char*)v; k = (int)L; for(rv = 0;; s += n) { if ((n = read(p->fr, s, k)) <= 0) return -1; rv += n; if ((k -= n) <= 0) break; } return rv; } static int Write(ProxProg *p, void *v, Uint L) { char *s; int k, n, rv; s = (char*)v; k = (int)L; for(rv = 0;; s += n) { if ((n = write(p->fw, s, k)) <= 0) return -1; rv += n; if ((k -= n) <= 0) break; } return rv; } #define SocRead Read #define SocWrite Write #define closesocket close #endif /*}}*/ static void CloseSocket(int sd) { shutdown(sd,2); closesocket(sd); } #define TM(len) (*ae->Tempmem)(TI->TMI,len) static char* strcpe(char *t, cchar *f) { while((*t++ = *f)) ++f; return t; } static void H_init(TP_Head *H, int job, TableInfo *TI) { memset(H, 0, sizeof(TP_Head)); memcpy(H->magic, TABLEPROXY_MAGIC, sizeof(H->magic)); H->arith = Arith_Kind_ASL; H->job = job; if (TI) { H->TIflags = TI->flags; H->arity = TI->arity; H->ncols = TI->ncols; } } #ifndef STAND_ALONE /*{{*/ static ProxProg * new_PP(AmplExports *ae, ProxyInfo *PI, TableInfo *TI, char *name, int port) { ProxProg *p; size_t L; L = strlen(name) + 1; p = (ProxProg*)Malloc1a(ae, sizeof(ProxProg) + L); strcpy(p->name = (char*)(p+1), name); if ((p->port = port) > 0) { p->prnext = PI->remote; PI->remote = p; } else { p->prnext = PI->progs; PI->progs = p; p->proto = 0; } return p; } static void cleanup(void *v) { MemBlock *mb, *mb1; ProxyInfo *Pi; ProxProg *p; Pi = (ProxyInfo*)v; for(p = Pi->progs; p; p = p->prnext) { Write(p, "Quit", 5); #ifdef _WIN32 CloseHandle(p->hw); CloseHandle(p->hr); #else close(p->fw); close(p->fr); #endif } #ifdef _WIN32 if (Pi->remotestarted) WSACleanup(); #endif for(mb1 = Pi->curmb; (mb = mb1); ) { mb1 = mb->u.next; free(mb); } memset(Pi, 0, sizeof(ProxyInfo)); } static void* Malloc3(AmplExports *ae, TableInfo *TI, size_t L, cchar *where) { void *rv = malloc(L); if (!rv) { sprintf(TI->Errmsg = TM(strlen(where) + 64), "malloc(%lu) failure in %s.", (unsigned long)L, where); } return rv; } #ifdef _WIN32 /*{{*/ #define PathName "Path" static int pipe(TableInfo *TI, HANDLE *fd) { SECURITY_ATTRIBUTES S; S.nLength = sizeof(S); S.lpSecurityDescriptor = 0; S.bInheritHandle = FALSE; if (CreatePipe(fd,fd+1,&S,4096)) return 0; TI->Errmsg = "CreatePipe failed!"; return 1; } static void inheritable(HANDLE Thisproc, HANDLE *ph) { HANDLE h1 = *ph; if (DuplicateHandle(Thisproc, h1, Thisproc, ph, 0, TRUE, DUPLICATE_SAME_ACCESS)) CloseHandle(h1); } static char * make_cmdline(AmplExports *ae, TableInfo *TI, char **a, int sep) { char *e0, *r, *rv; char **a0, *s, *s0, *s1; int k, k0; a0 = a; k = 2; if (sep) while((s = *a++)) { s0 = s; for(;;) { while(*s > ' ') { if (*s++ == '"') ++k; } if (!*s) break; k += 2; while(*++s <= ' ') if (!*s) goto break2; } break2: k += s - s0 + 1; } else while((s = *a++)) k += strlen(s) + 1; e0 = 0; k0 = k; if (!sep) { s1 = e0 = GetEnvironmentStrings(); while(*(s = s1++)) { while(*s1++); if (s[0] == '=') k += s1 - s; } } if (!(r = rv = (char*)Malloc3(ae,TI,k,"make_cmdline"))) return 0; if (k > k0) { for(s1 = e0; *(s = s1++); ) { while(*s1++); if (*s == '=') while((*r++ = *s++)); } } if (e0) FreeEnvironmentStrings(e0); if (sep && (s = *a0)) { while(*s) if (*s++ <= ' ') { *r++ = '"'; for(s = *a0; (*r = *s++); ++r); *r++ = '"'; if (*++a0) *r++ = sep; break; } } while((s = *a0++)) { while((*r = *s++)) ++r; *r++ = sep; } *r = r[1] = 0; if (r > rv) r[-1] = 0; return rv; } #else /*}{*/ #define PathName "PATH" #endif /*}}*/ static char * findprog(AmplExports *ae, char *prog, char *b, size_t buflen) { char *b1, *p, *p0, *s; size_t L; struct stat sb; #ifdef _WIN32 #define SEP ';' if (!stat(prog,&sb)) return prog; if (*prog && prog[1] == ':') goto trystat; #else /*{*/ #define SEP ':' static gid_t gid; static uid_t uid; #ifndef S_IXUSR #define S_IXUSR 0100 #define S_IXGRP 010 #define S_IXOTH 1 #endif #ifndef S_IFDIR #define S_IFDIR 040000 #endif #endif /*}*/ for(s = prog; *s; ++s) { #ifdef _WIN32 if (*s == '/' || *s == '\\') { /*}*/ trystat: #else if (*s == '/') { #endif if (stat(prog, &sb)) return 0; return prog; } } if (!(p = getenv(PathName))) return p; #ifndef _WIN32 if (!gid) { gid = getegid(); uid = geteuid(); } #endif L = strlen(prog) + 2; while(*p) { p0 = p; while(*p != SEP && *p) ++p; if (p0 == p || (*p0 == '.' && p0+1 == p)) b1 = prog; else if (L + (p-p0) > buflen) b1 = 0; else { for(b1 = b; p0 < p;) *b1++ = *p0++; while(b1 > b && b1[-1] == SLASH) --b1; *b1++ = SLASH; strcpy(b1, prog); b1 = b; } if (*p == SEP) ++p; if (b1 && !stat(b1, &sb)) { #ifndef _WIN32 if (sb.st_mode & S_IFDIR) continue; if (sb.st_uid == uid) { if (!(sb.st_mode & S_IXUSR)) continue; } else if (sb.st_gid == gid) { if (!(sb.st_mode & S_IXGRP)) continue; } else if (!(sb.st_mode & S_IXOTH)) continue; #endif if (b1 == prog) b = prog; return b; } } return 0; } static int get_sd(AmplExports *ae, TableInfo *TI, char *ipstr, int port, int proto) { int rc, sd; size_t L; struct hostent *ptrh; struct sockaddr_in sab; rc = -1; memset(&sab, 0, sizeof(sab)); sab.sin_family = AF_INET; sab.sin_port = htons((short)port); if (!(ptrh = gethostbyname(ipstr))) { L = strlen(ipstr); sprintf(TI->Errmsg = TM(L+40), "Bad IP address \"%s\".", ipstr); goto ret; } if ((sd = socket(PF_INET, SOCK_STREAM, proto)) < 0) { TI->Errmsg = "socket(PF_INET,...) failed."; goto ret; } memcpy(&sab.sin_addr, ptrh->h_addr, ptrh->h_length); if (connect(sd, (struct sockaddr *) &sab, sizeof(sab)) < 0) { L = strlen(ipstr); sprintf(TI->Errmsg = TM(L+64), "Could not connect to port %d of \"%s\".", port, ipstr); } else rc = sd; ret: return rc; } static ProxProg* startremote(AmplExports *ae, TableInfo *TI, ProxyInfo *PI, char *ipstr) { ProxProg *pp; char buf[256], *s, *se; int port, sd; size_t L; struct protoent *ptrp; port = 5196; L = strlen(ipstr); if (L >= sizeof(buf)) { sprintf(TI->Errmsg = TM(L + 40), "Oversize 'remote=...' value \"%s\".", ipstr); return 0; } if ((s = strchr(ipstr, '@'))) { L = s - ipstr; memcpy(buf, ipstr, L); buf[L] = 0; ipstr = buf; port = (int)strtol(++s,&se,10); if (port <= 0 || *se) { sprintf(TI->Errmsg = TM(strlen(s) + 40), "Bad port number \"%s\".", s); return 0; } } if (!PI->remotestarted) { #ifdef _WIN32 if (wsa_startup()) { TI->Errmsg = "WSAStartup failed."; return 0; } #endif if (!(ptrp = getprotobyname("tcp"))) { TI->Errmsg = "getprotobyname(\"tcp\") failed."; return 0; } PI->proto = ptrp->p_proto; PI->remotestarted = 1; } if ((sd = get_sd(ae, TI, ipstr, port, PI->proto)) < 0) return 0; pp = new_PP(ae, PI, TI, ipstr, port); pp->fr = pp->fw = sd; pp->read = SocRead; pp->write = SocWrite; pp->proto = PI->proto; return pp; } static ProxProg* startprog(AmplExports *ae, TableInfo *TI, ProxyInfo *PI, char *prog) { ProxProg *pp; char *av[3], buf[4096], **ep, *p; #ifdef _WIN32 /*{{*/ HANDLE Thisproc, fd0[2], fd1[2], hsave[2]; PROCESS_INFORMATION Pi; STARTUPINFO Si; char *cmdline, *d, *env, pbuf[4096], *prog0, *s; int b; size_t L; void(*oldsig)(int); /* if prog does not end with ".exe", append ".exe" */ prog0 = prog; d = 0; for(s = prog; *s; ++s) if (*s == '.') d = s; if (!d || (s - d) != 4 || (d[1] != 'e' && d[1] != 'E') || (d[2] != 'x' && d[2] != 'X') || (d[3] != 'e' && d[3] != 'E')) { if ((L = s - prog) >= sizeof(pbuf) - 5) return 0; memcpy(pbuf, prog, L); strcpy(pbuf+L, ".exe"); prog = pbuf; } #else /*}{*/ int fd0[2], fd1[2]; pid_t pid; #endif /*}}*/ if (!(p = findprog(ae, prog, buf, sizeof(buf)))) { sprintf(TI->Errmsg = TM(strlen(prog) + 32), "Could not find \"%s\".", prog); return 0; } av[0] = p; av[1] = "--local"; av[2] = 0; ep = ae->ASLdate >= 20111028 && !ae->asl ? (char**)getenv(0) : 0; #ifdef _WIN32 /*{{*/ if (!(cmdline = make_cmdline(ae, TI, av, ' '))) return 0; if (!(env = make_cmdline(ae, TI, ep, 0))) { free_cl: free(cmdline); return 0; } if (pipe(TI, fd0)) { free_e: free(env); goto free_cl; } if (pipe(TI, fd1)) { CloseHandle(fd0[0]); CloseHandle(fd0[1]); goto free_e; } memset(&Si,0,sizeof(Si)); Si.cb = sizeof(Si); Si.wShowWindow = SW_SHOW; Thisproc = GetCurrentProcess(); inheritable(Thisproc, &fd0[1]); inheritable(Thisproc, &fd1[0]); hsave[0] = GetStdHandle(STD_INPUT_HANDLE); hsave[1] = GetStdHandle(STD_OUTPUT_HANDLE); SetStdHandle(STD_INPUT_HANDLE, fd1[0]); SetStdHandle(STD_OUTPUT_HANDLE, fd0[1]); oldsig = signal(SIGINT, SIG_IGN); b = CreateProcess(p,cmdline,0,0,1,0,env,0,&Si,&Pi); SetStdHandle(STD_INPUT_HANDLE, hsave[0]); SetStdHandle(STD_OUTPUT_HANDLE, hsave[1]); if (!b) { sprintf(TI->Errmsg = TM(strlen(p) + 64), "CreateProcess(\"%s\") failure!\nError code %ld.\n", p, GetLastError()); return 0; } CloseHandle(Pi.hThread); CloseHandle(fd1[0]); CloseHandle(fd0[1]); CloseHandle(Pi.hProcess); free(env); free(cmdline); pp = new_PP(ae, PI, TI, prog0, 0); pp->hr = fd0[0]; pp->hw = fd1[1]; #else /*}{*/ if (pipe(fd0)) { badpipe: TI->Errmsg = "pipe failure"; return 0; } if (pipe(fd1)) { close(fd0[0]); close(fd0[1]); goto badpipe; } if ((pid = fork()) == 0) { /* child */ dup2(fd0[1], 1); dup2(fd1[0], 0); close(fd0[0]); close(fd0[1]); close(fd1[0]); close(fd1[1]); signal(SIGINT, SIG_IGN); execve(p, av, ep); fprintf(Stderr, "execve(\"%s\",...) failed!\n", p); exit(1); } close(fd0[1]); close(fd1[0]); pp = new_PP(ae, PI, TI, prog, 0); pp->fr = fd0[0]; pp->fw = fd1[1]; #endif /*}}*/ pp->read = Read; pp->write = Write; return pp; } static int hsetup(AmplExports *ae, TableInfo *TI, ProxyInfo **pPI, ProxProg **pp, int *nsu) { ProxProg *p; ProxyInfo *PI; char *hname, *ip, *lib, *prog, *s, *s1, *se, **strs, **strs0, **strse, *verb; int i, rowchunk, sd, verbose; strs = TI->strings; strse = strs + TI->nstrings; if (strs >= strse || strcmp(strs[0], "tableproxy")) return DB_Refuse; strs0 = strs; ip = lib = prog = verb = 0; rowchunk = 512; verbose = 0; hname = 0; if (++strs >= strse) { TI->Errmsg = "Two few strings before \":[...]\"; for more on the strings,\n" "use the AMPL command\n\n\tprint _handler_desc['tableproxy'];"; return DB_Error; } *pPI = PI = (ProxyInfo*)TI->Vinfo; verbose = 0; for(;;++strs) { if (strs >= strse) { TI->Errmsg = "No handler name for the proxy table handler."; return DB_Error; } s = s1 = *strs; while(*s1 != '=') { if (!*s1++) goto break2; } ++s1; switch(*s) { case 'I': if (strncmp(s,"IP=",3)) { badv: sprintf(TI->Errmsg = TM(strlen(s) + 24), "Bad string '%s'.", s); return DB_Error; } ipset: if (prog) goto notboth; ip = s1; continue; case 'h': if (strncmp(s,"hname=",6)) goto badv; hname = s + 6; continue; case 'i': if (strncmp(s,"ip=",3)) goto badv; goto ipset; case 'l': if (strncmp(s,"lib=",4)) goto badv; if (lib) { TI->Errmsg = "Two \"lib=...\" assignments seen; expected at most one."; return DB_Error; } lib = s; continue; case 'p': if (strncmp(s,"prog=",5)) goto badv; if (ip) { notboth: TI->Errmsg = "Both \"ip=...\" and \"prog=...\" seen."; return DB_Error; } if (prog) { TI->Errmsg = "Two \"prog=...\" assignments seen; expected at most one."; return DB_Error; } prog = s1; continue; case 'r': if (strncmp(s,"rowchunk=",9)) goto badv; i = (int)strtol(s1,&se,10); if (i <= 0 || *se) goto badv; rowchunk = i; continue; case 'v': if (strncmp(s,"verbose=",8)) goto badv; verb = s; verbose = (int)strtol(s1, &se, 10); if (se <= s1) goto badv; continue; default: goto badv; } } break2: if (ip) { for(p = PI->remote; p; p = p->prnext) { if (!strcmp(ip, p->name)) { if ((sd = get_sd(ae, TI, p->name, p->port, p->proto)) < 0) return DB_Error; p->fr = p->fw = sd; goto found; } } if ((p = startremote(ae, TI, PI, ip))) goto found; return DB_Error; } if (!prog) prog = "tableproxy" Bits; for(p = PI->progs; p; p = p->prnext) { if (!strcmp(prog, p->name)) goto found; } if (!(p = startprog(ae, TI, PI, prog))) return DB_Error; found: *pp = p; if (!hname) hname = *strs; if (lib) *--strs = lib; *nsu = strs - strs0; p->rowchunk = rowchunk; p->verbose = verbose; p->hname = hname; return 0; } static size_t length(char **ps, int n) { int i; size_t k; k = n; for(i = 0; i < n; ++i) k += strlen(ps[i]); return k; } static char * cpall(char *s, char **ps, int n) { int i; for(i = 0; i < n; ++i) s = strcpe(s, ps[i]); return s; } #ifndef ReadGulp #define ReadGulp 1000 #endif enum { Rgulp = ReadGulp }; static int Read_ampl_proxy(AmplExports *ae, TableInfo *TI) { DbCol *dbc, *dbc0, *dbce; IRpair *irp; ProxyInfo *PI; ProxProg *p; TP_Head *H, H0; Uint Lt, i, is, *itab, *itst0, j, nitab, nr, stlen, *z; char *s, *stab, **strs, **sv, **sv0, *x[2]; int k, nc, nc1, nstr, nsu, rc; real *rtab, *rtab0; size_t L, Ls; if ((rc = hsetup(ae,TI,&PI,&p,&nsu))) return rc; nstr = TI->nstrings - nsu; strs = TI->strings + nsu; nc = TI->ncols; nc1 = nc + TI->arity; stlen = strlen(TI->tname) + 1 + strlen(p->hname) + 1 + length(strs, nstr) + length(TI->colnames, nc1); L = sizeof(TP_Head) + stlen; rc = DB_Error; if (!(H = (TP_Head*)Malloc3(ae,TI,L,"Read_ampl_proxy"))) goto ret; H_init(H, TPx_Read, TI); H->nrcols = H->nscols = H->nrtab = H->nitab = 0; H->tablen = stlen; H->nstrings = nstr; H->nrows = p->rowchunk; s = stab = (char*)(H+1); s = strcpe(s, p->hname); s = strcpe(s, TI->tname); s = cpall(s, strs, nstr); s = cpall(s, TI->colnames, nc1); k = p->write(p, H, L); free(H); if (k < 0) { TI->Errmsg = "p->write failure"; goto ret; } readmore: if ((k = p->read(p, &H0, sizeof(H0))) < 0) { TI->Errmsg = "p->read failure"; goto ret; } if (H0.job == TPx_Error) { if ((Lt = H0.tablen) > 0) { TI->Errmsg = (char*)TM(Lt); if ((k = p->read(p, TI->Errmsg, Lt)) < 0) TI->Errmsg = "p->read_2 error-read failure"; } else TI->Errmsg = "Error return with no error message."; goto ret; } if (!(nr = H0.nrows)) goto done; if (H0.ncols != nc) { TI->Errmsg = "H0.ncols error"; goto ret; } nitab = 2*(H0.ncols + H0.arity) + nr*H0.nscols; L = nc1 * sizeof(DbCol); Ls = 0; if (H0.nscols) Ls = (nr * H0.nscols * sizeof(char*) + sizeof(real) - 1) & ~(sizeof(real) - 1); if (!(dbc0 = (DbCol*)Malloc3(ae,TI, L + Ls + H0.tablen,"Read_ampl_proxy read alloc"))) goto ret; dbc = dbc0; sv0 = (char**)(dbc + nc1); rtab = (real*)((char*)sv0 + Ls); if (p->read(p, rtab, H0.tablen) < 0) { free(dbc0); TI->Errmsg = "2nd read in Read_ampl_proxy failed."; goto ret; } itab = (Uint*)(rtab + H0.nrtab); stab = (char*)(itab + H0.nitab); if (H0.nitab > nitab) { s = stab + itab[nitab]; if (*s) printf("%s", s); if (!nr) { free(dbc0); goto done; } } itst0 = itab + 2*nc1 - nr; irp = (IRpair*)itab; dbce = dbc + nc1; memset(dbc, 0, nc1*sizeof(DbCol)); rtab0 = rtab - nr; sv0 -= nr; x[0] = 0; x[1] = TI->Missing; for(; dbc < dbce; ++dbc, ++irp) { if ((j = irp->ir)) dbc->dval = rtab0 + j*nr; if ((j = irp->is)) { j *= nr; dbc->sval = sv = sv0 + j; z = itst0 + j; for(i = 0; i < nr; ++i) sv[i] = (is = z[i]) < 2 ? x[is] : stab + is; } } TI->AddRows(TI, dbc0, nr); free(dbc0); if (!(H0.job & TPx_Done)) goto readmore; if (TI->Errmsg) { rc = DB_Error; goto ret; } done: rc = DB_Done; ret: if (p->port > 0) CloseSocket(p->fr); return rc; } static int Write_ampl_proxy(AmplExports *ae, TableInfo *TI) { DbCol *db, *db0, *dbe; ProxProg *p; ProxyInfo *PI; TP_Head *H, H0; Uint ir, is, *itab, *itc; char *Missing, *s, *s0, *s1, **strs, **sv, **sve; int nc, nc1, nrc, nsc, nstr, nsu, rc; long nr; real *r; size_t L, itlen, rclen, rtlen, stlen; if ((rc = hsetup(ae,TI,&PI,&p,&nsu))) return rc; rc = DB_Error; nc = TI->ncols; nc1 = nc + TI->arity; db0 = TI->cols; dbe = db0 + nc1; nstr = TI->nstrings - nsu; strs = TI->strings + nsu; stlen = strlen(TI->tname) + 1 + strlen(p->hname) + 1 + length(strs, nstr) + length(TI->colnames, nc1); nrc = nsc = 0; nr = TI->nrows; Missing = TI->Missing; for(db = db0; db < dbe; ++db) { if (db->dval) ++nrc; if ((sv = db->sval)) { ++nsc; for(sve = sv + nr; sv < sve; ++sv) if ((s = *sv) && s != Missing) stlen += strlen(s) + 1; } } itlen = 2*nc1 + nsc*nr; rtlen = nrc*nr; L = sizeof(TP_Head) + rtlen*sizeof(real) + itlen*sizeof(Uint) + stlen; if (!(H = (TP_Head*)Malloc3(ae, TI, L, "Write_ampl_proxy"))) goto ret; H_init(H, TPx_Write, TI); H->nrcols = nrc; H->nscols = nsc; H->tablen = L - sizeof(TP_Head); H->nstrings = nstr; H->nrows = nr; H->nrtab = rtlen; H->nitab = itlen; r = (real*)(H+1); itc = (Uint*)(r + rtlen); itab = itc + 2*nc1; memset(itc, 0, itlen*sizeof(Uint)); s = s0 = (char*)(itc + itlen); s = strcpe(s, p->hname); s = strcpe(s, TI->tname); s = cpall(s, strs, nstr); s = cpall(s, TI->colnames, nc1); rclen = nr*sizeof(real); ir = is = 0; for(db = db0; db < dbe; itc += 2, ++db) { if (db->dval) { itc[0] = ++ir; memcpy(r, db->dval, rclen); r += nr; } if ((sv = db->sval)) { itc[1] = ++is; for(sve = sv + nr; sv < sve; ++itab, ++sv) { if ((s1 = *sv)) { if (s1 == Missing) *itab = 1; else { *itab = s - s0; s = strcpe(s, s1); } } } } } if (p->write(p, H, L) < 0) { TI->Errmsg = "write error in Write_ampl_proxy"; goto ret; } if (p->read(p, &H0, sizeof(TP_Head)) < 0) { TI->Errmsg = "read error in Write_ampl_proxy"; goto ret; } if (memcmp(&H0, TABLEPROXY_MAGIC, sizeof(H->magic)) || !(H0.job & (TPx_Done|TPx_Error))) { TI->Errmsg = "unexpected reply in Write_ampl_proxy"; goto ret; } if (H0.job & TPx_Error) { if (p->read(p, TI->Errmsg = TM(H0.tablen), H0.tablen) != H0.tablen) TI->Errmsg = "error reading error return in Write_ampl_proxy"; goto ret; } if (H0.nitab) { if (p->read(p, s = TM(H0.tablen), H0.tablen) != H0.tablen) { TI->Errmsg = "error reading verbose output in Write_ampl_proxy"; goto ret; } itab = (Uint*)s; s = (char*)(itab + H0.nitab) + itab[0]; if (*s) printf("%s", s); } rc = DB_Done; ret: if (p->port > 0) CloseSocket(p->fr); if (H) free(H); return rc; } #ifdef OTHER_FUNCADD extern void OTHER_FUNCADD(AmplExports*); #endif void funcadd(AmplExports *ae) { static char info[] = "tableproxy\n" "Proxy table handler for using local " Bits "-bit table handlers\n" "and handlers on remote machines (version " TableProxyVersion ").\n" "Strings expected before \":[...]\":\n" " 'tableproxy' Connection ...\n" "where ... are strings for the other handler. Connection can involve\n" "zero or more of\n\n" " 'ip=...[@ppp]'\n" " 'prog=...'\n" " 'hname=...'\n" " 'rowchunk=mmm'\n" " 'lib=...'\n\n" "At most one of 'prog=...' and 'ip=...' may appear.\n" "The ... in \"prog=...\" is the desired local program\n" "The ... in \"ip=...\" is a remote IP address at which a proxy table handler\n" "is running; the ppp in \"@ppp\", if present, is the IP port to use\n" "(default = 5196).\n" "The ... in 'hname=...' is the handler name (seen in \"display _HANDLERS;\"); if\n" "not given, hname is assumed to be the first string in the strings for the other\n" "handler (i.e., the ... following Connection).\n" "For reading tables, the mmm in 'rowchunk=mmm' is the maximum number of rows for\n" "the remote proxy to cache before sending them to the local proxy (default 512).\n" "The ... in \"lib=...\" is a shared library in which the remote proxy should\n" "look for a suitable handler. If neither 'ip=' nor 'prog=' appears,\n" "'prog=tableproxy" Bits "' is assumed."; static int first = 1; /* Inform AMPL about the proxy handler. */ if (first) { first = 0; at_exit(cleanup, &PI0); } #ifdef OTHER_FUNCADD OTHER_FUNCADD(ae); #endif add_table_handler(Read_ampl_proxy, Write_ampl_proxy, info, 0, &PI0); } #else /*} STAND_ALONE {*/ static char *pfc, *pfc1, *pfce; static void pfc_alloc(size_t *Lp) { char *t; size_t L1, L2; L1 = pfc1 - pfc; if (!pfc) L2 = 4096; else L2 = 2*(pfce - pfc); t = (char*)Malloc(L2); if (pfc) { memcpy(t, pfc, L1); free(pfc); } pfc = t; pfc1 = t + L1; pfce = t + L2; *Lp = pfce - pfc1; } static int printf_capture(cchar *fmt, ...) { int n; size_t L; va_list ap; L = pfce - pfc1; if (L < 128) pfc_alloc(&L); va_start(ap, fmt); n = vsnprintf(pfc1, L, fmt, ap); if (n >= L) { do pfc_alloc(&L); while(n >= L); n = vsnprintf(pfc1, L, fmt, ap); } pfc1 += n; return n; } static void* Malloc1(size_t L) { MemBlock *mb; size_t La; void *rv; La = PI0.mend - PI0.mnext; L = (L + sizeof(real) - 1) & ~(sizeof(real) - 1); if (La < L) { if (L >= 2048) { mb = (MemBlock*)Malloc(L + sizeof(MemBlock)); rv = (void*)(mb+1); } else { mb = (MemBlock*)Malloc(4096 + sizeof(MemBlock)); rv = (void*)(mb+1); PI0.mnext = (char*)rv + L; PI0.mend = (char*)rv + 4096; } mb->u.next = PI0.curmb; PI0.curmb = mb; } else { rv = (void*)PI0.mnext; PI0.mnext += L; } return rv; } #ifdef MDEBUG static int (*PRintf)(cchar *, ...); static int (*FFlush)(FILE*); static FILE *STDout; int mdbzork, mdbzork1; static void Report(cchar *what, void *v) { PRintf("%d %s: %x\n", ++mdbzork, what, v); if (mdbzork == mdbzork1) PRintf(""); FFlush(STDout); } #else #define Report(a,b) /*nothing*/ #endif static char Abits[] = "_" Bits1; typedef int (*DbRW)(AmplExports *ae, TableInfo *TI); typedef struct HandlerInfo { struct HandlerInfo *nexth; DbRW DbRead; DbRW DbWrite; cchar *info; cchar *hname; cchar *libname; /* name without "_32" or "_64" before final "." */ cchar *libname_alt; /* name with "_32." or "_64." (NULL if no ".") */ cchar *libname_found; cchar *libname_sought; Uint libname_len; Uint libname_altlen; Uint libname_foundlen; Uint libname_soughtlen; void *Vinfo; int flags; } HandlerInfo; struct Element { void **v; char *vk; int elno; }; typedef struct Tcache { /* description of a cached column during table read */ int nr; /* number of entries (only zero versus nonzero matters) */ int ns; /* number of string entries (ditto) */ real *rc; char **sc; } Tcache; enum {Sblen = 8182}; typedef struct Sblock { /* for string storage during table read */ struct Sblock *nextsb; size_t blen; /* actual length of sbuf */ char *sbf; /* next available byte */ char *sbe; /* sbe - sbf == bytes still available in this block */ /*char sbuf[0];*/ } Sblock; typedef struct THelp { TP_Head tph; AVL_Tree *at; /* for Lookup_w */ AmplExports *ae; HandlerInfo *HI; ProxProg *P; TMInfo *tmi; TableInfo *TI; cchar *errmsg; char *tabname; Tcache *tc; Sblock *cursb; Sblock *bigsb; Sblock *freesb; AVL_Tree *sbt; /* for large Sblocks (for huge strings) */ Element E; long maxrows, nr0; /* for reading, nr0 = rows so far in this chunk */ /* for writing, nr0 = number of rows in write table command */ /* (possibly to be augmented by rows in the existing table) */ size_t slen; /* length of strings in this block */ int maxcols; int ndcols, nscols; int ntcols; /* total number of cols: TI->ncols + TI->arity */ int needswap; } THelp; typedef union { struct sockaddr sa; struct sockaddr_in sa4; #ifndef NO_sockaddr_in6 struct sockaddr_in6 sa6; #endif } Saddr; extern int libload_ASL(AmplExports *ae, cchar *s, int ns, int warn); static HandlerInfo *Handlers, **NextHandler = &Handlers; static TMInfo TMI; static char Loopback[] = "127.0.0.1", Missing[1]; static char *aalast, *aanext, *curdir; static cchar *libname, *libname0, *libname_alt, *libname_found; static cchar *libname_sought, *libname0_bits, *libname0_dot, *lnspec; static int curdir_len, libname_altlen, libname_len; static int libname_foundlen, libname_soughtlen, sigcaught; static void sighand(int sig) { sigcaught = sig; } static void* Temp_mem(TMInfo *tmi, size_t L) { TMInfo *tm; tm = (TMInfo*)Malloc(sizeof(TMInfo) + L); Report("Temp_mem", tm); tm->u.prev = tmi->u.prev; tmi->u.prev = tm; return (void*)(tm+1); } static void ignore_addfunc(cchar *name, rfunc f, int type, int nargs, void *funcinfo, AmplExports *ae) {} static void get_dot(cchar *name, cchar **ndot, cchar **bitsuf) { cchar *dot, *s; dot = 0; for(s = name; *s; ++s) if (*s == '.') dot = s; *ndot = dot; if (dot && dot - name > 3 && dot[-3] == '_' && dot[-2] == Abits[1] && dot[-1] == Abits[2]) *bitsuf = dot - 3; else *bitsuf = 0; } #ifdef _WIN32 /*{{*/ static void lc_map(char *s) { int c; while((c = *s)) { if (c >= 'A' && c <= 'Z') *s = c + 'a' - 'A'; ++s; } } static int Abspath(cchar *s) { int c = *s; if (((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) && s[1] == ':' && ((c = s[2]) == '\\' || c == '/')) return 1; return 0; } static int has_slash(cchar *s) { int c; while((c = *s++)) if (c == '/' || c == '\\') return 1; return 0; } #define Getcwd(a,b) GetCurrentDirectory(b,a) #else /*}{*/ #define lc_map(s) /* do nothing */ #define Abspath(s) (*(s) == '/') #define Getcwd getcwd static int has_slash(cchar *s) { while(*s) if (*s++ == '/') return 1; return 0; } #endif /*}}*/ static void my_add_table_handler(DbRW DbRead, DbRW DbWrite, char *info, int flags, void *Vinfo) { HandlerInfo *H; size_t L, L0, L1, Ls; char *s; cchar *bitsuf, *dot; if (!info) return; while(*info <= ' ') if (!*info++) return; L0 = Ls = 0; if (!libname) { L0 = libname_len + 1; if (libname0_dot) L0 += libname_len + (libname0_bits ? -3 : 4); Ls = strlen(lnspec); L0 += Ls + 1; bitsuf = dot = 0; get_dot(lnspec, &dot, &bitsuf); if (dot) L0 += Ls + 4; } for(L = 0; info[L] > ' '; ++L); *NextHandler = H = (HandlerInfo*)Malloc1(sizeof(HandlerInfo) + L0 + L + 1); NextHandler = &H->nexth; H->nexth = 0; H->hname = s = (char*)(H+1); memcpy(s, info, L); s[L] = 0; if (!libname) { libname = s += L + 1; s = strcpe(s, libname0); libname_found = libname; libname_foundlen = libname_len; libname_alt = 0; libname_altlen = 0; if (libname0_dot) { if (libname0_bits) { libname_alt = libname; libname = s; memcpy(s, libname0, L1 = libname0_bits - libname0); s = strcpe(s + L1, libname0_dot); libname_altlen = libname_len; libname_len -= 3; } else { libname_alt = s; memcpy(s, libname0, L1 = libname0_dot - libname0); s = strcpe(s + L1, Abits) - 1; s = strcpe(s, libname0_dot); libname_altlen = libname_len + 3; } } if (!strcmp(libname, libname_sought)) { libname_sought = libname; libname_soughtlen = libname_len; } else { libname_sought = libname_alt; libname_soughtlen = libname_altlen; } if (Ls) { if (bitsuf) { libname_alt = s; s = strcpe(s, lnspec); libname_altlen = Ls; L1 = bitsuf - lnspec; libname = s; memcpy(s, lnspec, L1); strcpy(s + L1, dot); libname_len = Ls - 3; } else { libname = s; s = strcpe(s, lnspec); libname_len = Ls; libname_altlen = 0; if (dot) { L1 = dot - lnspec; libname_alt = s; memcpy(s, lnspec, L1); s = strcpe(s+L1, Abits) - 1; strcpy(s, dot); libname_altlen = Ls + 3; } } } } while(info[L] && info[L] <= ' ') ++L; H->info = info + L; H->DbRead = DbRead; H->DbWrite = DbWrite; H->flags = flags; H->Vinfo = Vinfo; H->libname = libname; H->libname_len = libname_len; H->libname_alt = libname_alt; H->libname_altlen = libname_altlen; H->libname_sought = libname_sought; H->libname_soughtlen = libname_soughtlen; H->libname_found = libname_found; H->libname_foundlen = libname_foundlen; note_libuse_ASL(); } void auxinfo_ASL(AmplExports *ae) { ae->asl = 0; ae->Addfunc = ignore_addfunc; ae->Add_table_handler = my_add_table_handler; } void af_libnamesave_ASL(AmplExports *ae, cchar *fullname, cchar *name, int nlen) { libname = 0; libname0 = fullname; libname_len = strlen(fullname); get_dot(fullname, &libname0_dot, &libname0_bits); } static void swap(void *v, size_t L, Uint n) { char *s, *s1, *s2; int t; s = (char*)v; while(n > 0) { --n; s1 = s; s += L; s2 = s - 1; do { t = *s1; *s1++ = *s2; *s2-- = t; } while(s1 < s2); } } static int usage(int rc) { fprintf(rc ? Stderr : stdout, "Usage:\n\n" " %s [--nofork] {job} [port=nnn] [lib [lib...]]\n\n" "to start, stop, or inquire about a tableproxy server, using port 5196 or,\n" "if given in a port=nnn assignment, port nnn. If given, {job} must be one of\n\n" "\tstart\n" "\tstop\n" "\trestart\n" "\tstatus [ip=IP_addr]\n\n" "If a {job} keyword is not given, \"start\" is assumed. If \"start\" is specified\n" "or assumed and lib arguments are given, the specified libraries are loaded\n" "initially and table handlers are sought in them; other libraries are loaded\n" "on request.\n\n" "For \"status\", the optional ip=IP_addr is for checking the status of a remote\n" "tableproxy server at the specified (symbolic or numeric) IP address.\n\n" #ifdef _WIN32 "--nofork ==> do not detach from the invoking console.\n\n" #else "--nofork ==> neither fork nor ignore SIGHUP, SIGINT, SIGQUIT.\n\n" #endif "When invoked for local use by tableproxy*.dll, the only command-line argument\n" "is \"--local\".\n\n", progname); return rc; } static int Lookup_r(real *r, char **sa, TableInfo *TI) { return 0; } static int el_comp(void *v, const Element *a, const Element *b) { char *ak, *ake, *bk, *sa, *sb; int j, k, n; real d; void **va, **vb; static int dk[2] = { -1, 1 }; n = *(int*)v; va = a->v; vb = b->v; ak = a->vk; bk = b->vk; ake = ak + n; while(ak < ake) { k = *ak++; if (k != *bk++) return dk[k]; if (k) { sa = (char*)*va++; sb = (char*)*vb++; if (sa == sb) continue; if (sa == Missing) return -1; else if (sb == Missing) return 1; if ((j = strcmp(sa, sb))) return j; } else { d = *(real*)*va++ - *(real*)*vb++; if (d < 0.) return -1; if (d > 0.) return 1; } } return 0; } static void* MAalloc0(size_t L) { TMInfo *tm; tm = Malloc(L + sizeof(TMInfo)); Report("MAlloc0", tm); tm->u.prev = TMI.u.prev; TMI.u.prev = tm; return (void*)(tm+1); } static void* MAalloc(size_t L) { void *v; if (L >= 256) return MAalloc0(L); L = (L + sizeof(real) - 1) & ~(sizeof(real)-1); if (aalast - aanext < L) { aanext = (char*) MAalloc0(4096); aalast = aanext + 4096; } v = aanext; aanext += L; return v; } static void MAfree(void* v) {} static int Lookup_w(real *r, char **sa, TableInfo *TI) { AVL_Tree *at; DbCol *db, *db0, *dbe; Element *e; THelp *th; char *Ronly, *Sonly, *ek, **sv, *vk; int a, j, nd, ns; long i, n; size_t L, L1, Le; void **v; th = (THelp*)TI->Private; a = TI->arity; e = &th->E; if (!(at = th->at)) { th->at = at = AVL_Tree_alloc2(&th->TI->arity, el_comp, MAalloc, MAfree); e->v = (void**)MAalloc(a*(sizeof(void*) + 3)); e->vk = (char*)(e->v + a); e->elno = -1; Ronly = e->vk + a; Sonly = Ronly + a; memset(Ronly, 0, a); memset(Sonly, 1, a); L = Le = sizeof(Element) + a*sizeof(void*); L1 = L + a; n = th->nr0; db0 = TI->cols; dbe = db0 + a; for(nd = ns = 0, db = db0; db < dbe; ++db) { if (db->dval) ++nd; if (db->sval) ++ns; } ek = vk = 0; if (!ns) ek = vk = Ronly; else if (!nd) ek = vk = Sonly; for(i = 0; i < n; ++i) { if (vk == 0) { nd = ns = 0; for(db = db0; db < dbe; ++db) { if ((sv = db->sval) && sv[i]) ++ns; else ++nd; } Le = L; if (!ns) ek = Ronly; else if (!nd) ek = Sonly; else { ek = 0; Le = L1; } } e = (Element*)MAalloc(Le); e->v = v = (void**)(e+1); e->vk = ek; db = db0; if (ek == Ronly) for(; db < dbe; ++db) *v++ = &db->dval[i]; else if (ek == Sonly) for(; db < dbe; ++db) *v++ = db->sval[i]; else { e->vk = ek = (char*)(v + a); for(; db < dbe; ++db, ++v, ++ek) { if ((sv = db->sval) && sv[i]) { *ek = 1; *v = sv[i]; } else { *ek = 0; *v = &db->dval[i]; } } } e->elno = i; AVL_insert(e, at); } e = &th->E; } ek = e->vk; v = e->v; j = 0; if (r) { if (sa) for(; j < a; ++j, ++v, ++ek) { if ((*v = sa[j])) *ek = 1; else { *ek = 0; *v = &r[j]; } } else for(; j < a; ++j) { *ek++ = 0; *v++ = &r[j]; } } else for(; j < a; ++j) { *ek++ = 1; *v++ = sa[j]; } if ((e = AVL_find(e, at))) return e->elno; return -1; } static char * err_msg(THelp *th, cchar *fmt, ...) { HandlerInfo *H; char *s; int i; size_t L; va_list ap; va_start(ap, fmt); H = th->HI; L = strlen(H->hname) + H->libname_soughtlen + strlen(fmt) + 512; s = MAalloc(L); i = snprintf(s, L, "Table handler %s in %s:\n", H->hname, H->libname_sought); vsnprintf(s+i, L-i, fmt, ap); return s; } static int sb_cmp(void *v, const Element *a, const Element *b) { Sblock *ba, *bb; ba = (Sblock*)a; bb = (Sblock*)b; if (ba->blen < bb->blen) return -1; if (ba->blen > bb->blen) return 1; return 0; } static void sbcopy(THelp *th, Tcache *tc, long j, char *s) { AVL_Node *N; AVL_Tree *sbt; Sblock *sb, sb0, *sb1; char *t; size_t L; L = strlen(s) + 1; if (L >= (Sblen >> 1)) { sb = 0; if ((sbt = th->sbt)) { sb0.blen = L; if ((sb = (Sblock*)AVL_first_ge(sbt, (Element*)&sb0, &N))) AVL_delnode(sbt, &N); } if (!sb) { sb = MAalloc0(sizeof(Sblock) + L); sb->blen = L; } sb->nextsb = th->bigsb; th->bigsb = sb; t = (char*)(sb + 1); } else { if (!(sb = th->cursb) || sb->sbe - sb->sbf < L) { if ((sb1 = th->freesb)) th->freesb = sb1->nextsb; else sb1 = (Sblock*)MAalloc0(sizeof(Sblock) + Sblen); sb1->sbf = (char*)(sb1 + 1); sb1->sbe = sb1->sbf + Sblen; sb1->nextsb = sb; th->cursb = sb = sb1; } t = sb->sbf; sb->sbf += L; } memcpy(tc->sc[j] = t, s, L); th->slen += L; } static int rtab_send(THelp *th, TableInfo *TI, int flags) { AVL_Tree *sbt; ProxProg *P; Sblock *sb, *sb1; TP_Head *H; Tcache *tc, *tce; Uint *cd, *cd0, *it; char *s, *s0, **sp, **spe, *t; cchar *emsg; int nitab, nr, nrtab, ns, ntc, rc; long m; real *r, *r0; size_t L; static char verbose_output[] = "\nVerbose table-handler output:\n"; m = th->nr0; th->nr0 = 0; if (flags & TPx_Error || (flags & TPx_Done && m == 0)) { r0 = 0; cd0 = 0; L = 0; emsg = 0; if (flags & TPx_Error) { if (!(emsg = th->errmsg)) emsg = "rtab_send has flags == TPx_Error, but th->errmsg == 0"; L = strlen(emsg) + 1; } if (pfc1 > pfc) { *pfc1 = 0; L += pfc1 - pfc + sizeof(verbose_output) + sizeof(Uint) + 2; } H = (TP_Head*)Malloc(sizeof(TP_Head) + L); H_init(H, flags, TI); if (emsg) { H->job = TPx_Error; s = (char*)(H+1); s = strcpe(s, emsg); if (pfc1 > pfc) { s = strcpe(s-1, verbose_output); s = strcpe(s-1, pfc); if (s[-2] != '\n') { s[-1] = '\n'; *s = 0; } pfc1 = pfc; } goto finish; } if (pfc1 > pfc) { H->nitab = 1; it = (Uint*)(H+1); s = s0 = (char*)(it + 1); } goto finish; } tc = th->tc; ntc = th->ntcols; tce = tc + ntc; for(nr = ns = 0; tc < tce; ++tc) { if (tc->nr) ++nr; if (tc->ns) ++ns; } nitab = 2*ntc + ns*m; if (pfc1 > pfc) ++nitab; nrtab = nr*m; L = nitab*sizeof(Uint) + nrtab*sizeof(real) + th->slen; if (pfc1 > pfc) L += pfc1 - pfc + 1; H = Malloc(sizeof(TP_Head) + L); H_init(H, TPx_Read | flags, TI); H->nrcols = nr; H->nscols = ns; H->nstrings = 0; H->nrows = m; H->nrtab = nrtab; H->nitab = nitab; r = r0 = (real*)(H+1); if (nrtab == 0) r0 = 0; cd = cd0 = (Uint*)(r + nrtab); it = cd + 2*ntc; s0 = (char*)(cd + nitab); s = strcpe(s0, TI->tname); nr = ns = 0; for(tc = th->tc; tc < tce; ++tc) { if (tc->nr) { tc->nr = 0; memcpy(r, tc->rc, m*sizeof(real)); r += m; *cd++ = ++nr; } else *cd++ = 0; if (tc->ns) { tc->ns = 0; *cd++ = ++ns; sp = tc->sc; spe = sp + m; while(sp < spe) { if (!(t = *sp++)) *it++ = 0; else if (t == Missing) *it++ = 1; else { *it++ = s - s0; s = strcpe(s, t); } } } else *cd++ = 0; } finish: if (pfc1 > pfc) { *it++ = s - s0; *pfc1 = 0; s = strcpe(s, pfc); pfc1 = pfc; } H->arith = th->tph.arith; H->tablen = L; L += sizeof(TP_Head); if (th->needswap) { if (r0) swap(r0, sizeof(real), H->nrtab); if (cd0) swap(cd0, sizeof(Uint), H->nitab); swap(&H->job, sizeof(Uint), TPH_n_Uint); } P = th->P; rc = 0; if (P->write(P, H, L) != L) { if (TI) TI->Errmsg = "write error in rtab_send"; rc = 1; } free(H); if (rc || flags & (TPx_Done|TPx_Error)) goto done; if ((sb = th->bigsb)) { if (!(sbt = th->sbt)) th->sbt = sbt = AVL_Tree_alloc2(0, sb_cmp, MAalloc, MAfree); do AVL_insert((Element*)sb, sbt); while((sb = sb->nextsb)); th->bigsb = 0; } for(sb = th->cursb; sb; sb = sb1) { sb1 = sb->nextsb; sb->nextsb = th->freesb; th->freesb = sb; } th->cursb = 0; done: return rc; } static void error_send(THelp *th, TableInfo *TI) { th->errmsg = TI->Errmsg; th->nr0 = 0; rtab_send(th, TI, TPx_Error); } static int add_rows_w(TableInfo *TI, DbCol *cols, long nrows) { /* When writing a table, TI->AddRows should not be called, */ /* so we give an error return. */ return 1; } static int add_rows_r(TableInfo *TI, DbCol *cols, long nrows) { DbCol *dbc, *dbce; Tcache *tc; THelp *th; char **ps, *s; int k, nr, ns; long i, j, m; real *r; size_t L; static char msg1[] = "cols[%d].dval == 0, but sval[%ld] == 0"; static char msg2[] = "cols[%d] has both dval == 0 and sval == 0"; th = (THelp*)TI->Private; /* allocate cache columns if necessary */ tc = th->tc; dbc = cols; dbce = dbc + th->ntcols; for(nr = ns = 0; dbc < dbce; ++dbc, ++tc) { if (dbc->dval && !tc->rc) ++nr; if (dbc->sval && !tc->sc) ++ns; } m = th->maxrows; if (nr + ns) { L = m*(nr*sizeof(real) + ns*sizeof(char*)); r = (real*)MAalloc(L); memset(r, 0, L); ps = (char**)(r + nr*m); tc = th->tc; for(dbc = cols; dbc < dbce; ++dbc, ++tc) { if (dbc->dval && !tc->rc) { tc->rc = r; r += m; } if (dbc->sval && !tc->sc) { tc->sc = ps; ps += m; } } } j = th->nr0; for(i = 0; i < nrows; ++i) { tc = th->tc; for(dbc = cols; dbc < dbce; ++dbc, ++tc) { r = dbc->dval; if ((ps = dbc->sval)) { if (!(s = ps[i])) { if (!r) { k = dbc - cols; TI->Errmsg = err_msg(th, msg1, k, i); return DB_Error; } goto add_r; } tc->ns++; if (s == Missing) { tc->sc[j] = s; } else sbcopy(th, tc, j, s); } else if (!r) { k = dbc - cols; TI->Errmsg = err_msg(th, msg2, k); return DB_Error; } else { add_r: tc->nr++; tc->rc[j] = r[i]; } } if (++j >= m) { th->nr0 = m; j = 0; if (rtab_send(th, TI, 0)) return DB_Error; } } th->nr0 = j; return 0; } static long Adjust_Maxrows(TableInfo *TI, long nmaxrows) { DbCol *dc, *dce; THelp *th; char **sv, **sv0; int nc, nd, ns; long nr; real *dv, *dv0; th = (THelp*)TI->Private; nc = TI->ncols + TI->arity; dc = TI->cols; dce = dc + nc; if (!th->maxrows) { for(nd = ns = 0; dc < dce; ++dc) { if (dc->dval) ++nd; if (dc->sval) ++ns; } th->ndcols = nd; th->nscols = ns; if (nmaxrows < 512) nmaxrows = 512; dc = TI->cols; th->maxrows = nmaxrows; } else { nd = th->ndcols; ns = th->nscols; if (nmaxrows < (th->maxrows <<= 1)) nmaxrows = th->maxrows; } dv = (real*)Temp_mem(th->tmi, nmaxrows*(nd*sizeof(real) + ns*sizeof(char*))); sv = (char**)(dv + nmaxrows*nd); nr = TI->nrows; for(; dc < dce; ++dc) { if ((dv0 = dc->dval)) { memcpy(dc->dval = dv, dv0, nr*sizeof(real)); dv += nmaxrows; } if ((sv0 = dc->sval)) { memcpy(dc->sval = sv, sv0, nr*sizeof(char*)); sv += nmaxrows; } } return TI->maxrows = nmaxrows; } static char * s_inc(char **ps) { char *rv, *s; rv = s = *ps; while(*s++); *ps = s; return rv; } static void* Col_Alloc(TableInfo *TI, int ncol, int sval) { DbCol *db; THelp *th; long nr; size_t L; void *v, **vp; th = (THelp*)TI->Private; if (!(nr = th->maxrows)) nr = TI->nrows; db = TI->cols + ncol; if (sval) { L = nr*sizeof(char*); vp = (void**)&db->sval; th->nscols++; } else { L = nr*sizeof(real); vp = (void**)&db->dval; th->ndcols++; } memset(*vp = v = (void*)Temp_mem(th->tmi, L), 0, L); return v; } static char * get_lbuf(cchar *ln, char *lbuf0, size_t lbuf_len, Uint *pls, cchar **nextdir) { Uint ln_len; char *lbuf, *s; cchar *cdir, *t, *t1; int q; size_t cdlen, L; lbuf = lbuf0; ln_len = strlen(ln); L = ln_len + 1; if (Abspath(ln) || has_slash(ln)) cdir = 0; else { if (!(cdir = *nextdir) && (cdir = getenv("ampl_libpath"))) { while(*cdir <= ' ' && *cdir) ++cdir; if (!*cdir) cdir = 0; } if (cdir) { if ((q = *cdir) == '"' || q == '\'') { for(t = ++cdir; *t != q; ++t) { if (!*t) goto use_curdir; } if (!(cdlen = t - cdir)) goto use_curdir; while(*++t <= ' ' && *t); *nextdir = *t ? t : 0; } else { for(t = cdir;;) { if (!*++t) { *nextdir = 0; break; } if (*t == '\n') { for(t1 = t+1; *t1 && *t1 <= ' '; ++t1); *nextdir = *t1 ? t1 : 0; break; } } while(t > cdir && t[-1] <= ' ') --t; cdlen = t - cdir; } } else { use_curdir: *nextdir = 0; if (!curdir && Getcwd(lbuf, lbuf_len)) { curdir_len = strlen(lbuf); strcpy(curdir = (char*)Malloc(curdir_len + 1), lbuf); lc_map(curdir); } cdir = curdir; cdlen = curdir_len; } } if (cdir) L += cdlen + 2; if (L > lbuf_len) lbuf = (char*)Malloc(L); s = lbuf; if (cdir) { memcpy(s, cdir, cdlen); s += cdlen; *s++ = SLASH; } strcpy(s, ln); lc_map(s); *pls = s - lbuf + ln_len; return lbuf; } static int strcomp(const char *a, const char *b) /* strcmp ignoring case */ { int c, d; while((c = *a++)) { if (!(d = *b++)) return 1; if (c == d) continue; if (c >= 'A' && c <= 'Z') c += 'a' - 'A'; if (d >= 'A' && d <= 'Z') d += 'a' - 'A'; if (c -= d) return c; } return *b ? -1 : 0; } static TableInfo * TI_init(THelp *th, Uint *itab, real *rtab, char *stab) { HandlerInfo *HI, *Hf, **pHI; TP_Head *h; TableInfo *TI; DbCol *dbc; Uint i, j, k, len, ls, lseen, nc, nc1, nr, ns, *stc, *sti; char *hname, *lbuf, lbuf0[4096], *s, *se, **sp, **st; cchar *cannot, *ln, *nextdir, *what; int m, ndcols, nscols; size_t L; th->errmsg = 0; th->maxrows = th->nr0 = 0; s = stab; hname = s_inc(&s); th->tabname = s_inc(&s); h = &th->tph; ns = h->nstrings; if (!strncmp(s,"lib=",4)) { s += 4; ln = s_inc(&s); --ns; } else ln = "ampltabl.dll"; lnspec = ln; len = strlen(ln); /* look for table hname */ Hf = 0; lseen = 0; cannot = nextdir = 0; sp = 0; /* silence buggy warning */ HI = Handlers; TI = 0; lbuf = lbuf0; tryagain: for(; HI; HI = HI->nexth) { if ((len != HI->libname_len || strcmp(ln, HI->libname)) && (len != HI->libname_altlen || strcmp(ln, HI->libname_alt))) continue; ++lseen; if (!strcomp(HI->hname, hname)) { Hf = HI; if (itab) { if (!HI->DbWrite) cannot = "write"; } else if (!HI->DbRead) cannot = "read"; break; } } if (!lseen++) { pHI = NextHandler; lbuf = get_lbuf(ln, lbuf0, sizeof(lbuf0), &ls, &nextdir); while((i = libload_ASL(th->ae, libname_sought = lbuf, ls, 2)) == 1 && nextdir) { if (lbuf != lbuf0) free(lbuf); lbuf = get_lbuf(ln, lbuf0, sizeof(lbuf0), &ls, &nextdir); } if (!i) { if ((HI = *pHI)) goto tryagain; what = "find any table handlers in"; } else what = i == 3 ? "find funcadd in" : "load"; L = ls + 80; th->errmsg = se = (char*)Temp_mem(th->tmi, L); snprintf(se, L, "Error with proxy table handler: could not %s \"%s\".", what, ln); goto ereply; } if (!(th->HI = Hf) || cannot) { L = strlen(hname) + 200; if (Hf) L += L + Hf->libname_soughtlen; th->errmsg = se = (char*)Temp_mem(th->tmi, L); m = snprintf(se, L, "Error with proxy table handler: no handler for \"%s\".", hname); if (Hf) m += snprintf(se+m, L-m, "\nDid find %s in \"%s\"" ",\nbut this handler cannot %s tables.\n", hname, Hf->libname_sought, cannot); ereply: th->nr0 = 0; rtab_send(th, 0, TPx_Error); goto ret; } nc = h->ncols; th->maxcols = th->ntcols = nc1 = nc + h->arity; nr = h->nrows; L = sizeof(TableInfo) + (nc1+ns)*sizeof(char*) + nc1*sizeof(DbCol); if (itab) { L += nr*h->nscols*sizeof(char*); TI = (TableInfo*)Temp_mem(th->tmi, L); } else { L += nc1*(sizeof(real) + sizeof(char*)); rtab = (real*)Temp_mem(th->tmi, L); memset(rtab, 0, nc1*(sizeof(real) + sizeof(char*))); sp = (char**)(rtab + nc1); TI = (TableInfo*)(sp + nc1); } th->TI = TI; memset(TI, 0, sizeof(TableInfo)); TI->tname = th->tabname; TI->strings = st = (char**)(TI+1); for(i = 0; i < ns; ++i) *st++ = s_inc(&s); TI->colnames = st; for(i = 0; i < nc1; ++i) *st++ = s_inc(&s); TI->Missing = Missing; TI->Vinfo = HI->Vinfo; TI->TMI = th->tmi; TI->nstrings = ns; TI->arity = h->arity; TI->ncols = nc; TI->flags = h->TIflags; TI->maxrows = TI->nrows = th->maxrows = nr; TI->Private = th; TI->AdjustMaxrows = Adjust_Maxrows; TI->ColAlloc = Col_Alloc; TI->cols = dbc = (DbCol*)(st + nr*h->nscols); memset(dbc, 0, nc1*sizeof(DbCol)); ndcols = nscols = 0; if (itab) { TI->AddRows = add_rows_w; TI->Lookup = Lookup_w; stc = itab + 2*nc1; for(i = 0; i < nc1; ++i) { if ((j = *itab++)) { dbc[i].dval = rtab + (j-1)*nr; ++ndcols; } if ((j = *itab++)) { ++nscols; --j; dbc[i].sval = sp = st + j*nr; sti = stc + j*nr; for(k = 0; k < nr; ++k) { switch(j = sti[k]) { case 0: s = 0; break; case 1: s = Missing; break; default: s = stab + j; } sp[k] = s; } } } } else { TI->AddRows = add_rows_r; TI->Lookup = Lookup_r; for(i = 0; i < nc1; ++i) { dbc[i].dval = rtab++; dbc[i].sval = sp++; } } th->ndcols = ndcols; th->nscols = nscols; ret: if (lbuf != lbuf0) free(lbuf); return TI; } static void rtab_read(THelp *h, ProxProg *P, char *stab) { HandlerInfo *H; TableInfo *TI; int k; size_t L; TI = TI_init(h, 0, 0, stab); if (!TI) return; H = h->HI; h->sbt = 0; h->cursb = h->bigsb = h->freesb = 0; h->slen = strlen(h->tabname) + 1; h->tc = 0; L = h->ntcols * sizeof(Tcache); memset(h->tc = (Tcache*)MAalloc(L), 0, L); switch(k = H->DbRead(h->ae, TI)) { case DB_Done: if (rtab_send(h, TI, TPx_Done)) goto badsend; break; case DB_Refuse: TI->Errmsg = err_msg(h, "\tRefused: missing handler name or database details."); goto badsend; default: TI->Errmsg = err_msg(h, "\tSurprise return %d from table reader.", k); /* no break */ case DB_Error: badsend: error_send(h, TI); break; } } static void wtab_write(THelp *h, ProxProg *P, real *rtab, Uint *itab, char *stab) { HandlerInfo *H; TableInfo *TI; int rc; TI = TI_init(h, itab, rtab, stab); if (!TI) return; h->nr0 = TI->nrows; h->at = 0; H = h->HI; if ((rc = H->DbWrite(h->ae, TI))) error_send(h, TI); else { h->nr0 = 0; rtab_send(h, TI, TPx_Write | TPx_Done); } } static int show_ver(void) { extern char sysdetails_ASL[]; printf("%s: tableproxy version " TableProxyVersion " (%s, ASL %ld)\n", progname, sysdetails_ASL, ASLdate_ASL); return 0; } static void freeall(THelp *th) { TMInfo *tm, *tmi; th->errmsg = 0; aanext = aalast = 0; tmi = th->tmi; tm = tmi->u.prev; tmi->u.prev = 0; while(tm) { Report("freeall", tm); tmi = tm->u.prev; free(tm); tm = tmi; } } static int quitreply(ProxProg *p, int sd) { TP_Head *H; size_t L; static char stopmsg[] = "Stopping"; H = (TP_Head*)Malloc1(L = sizeof(TP_Head) + sizeof(stopmsg)); H_init(H, TPx_quit, 0); strcpy((char*)(H+1), stopmsg); H->tablen = sizeof(stopmsg); p->write(p, H, L); CloseSocket(p->fr); return 0; } static void show_status(ProxProg *p) { HandlerInfo *HI; TP_Head *H; char *s, *s0, *se; int kh, nh; size_t L, L1, Lh; nh = 0; Lh = 8; L = sizeof(TP_Head) + 128; for(HI = Handlers; HI; HI = HI->nexth) { ++nh; L1 = strlen(HI->hname); if (Lh < L1) Lh = L1; L += L1 + HI->libname_foundlen; } H = (TP_Head*)MAalloc(L1 = (nh+1)*(Lh + 2) + L); s = s0 = (char*)(H+1); se = (char*)H + L; kh = Lh; if (nh <= 0) s += snprintf(s, se-s, "No table handlers loaded.\n"); else { s += snprintf(s, se-s, "%d table handler%s available:\nhandler %*s library\n", nh, nh == 1 ? "" : "s", kh-7, ""); for(HI = Handlers; HI; HI = HI->nexth) s += snprintf(s, se-s, "%-*s %s\n", kh, HI->hname, HI->libname_found); } H_init(H, TPx_status, 0); H->tablen = s - s0; p->write(p, H, sizeof(TP_Head) + H->tablen); } enum { start = 0, stop, status, restart }; static int dojob(AmplExports *ae, ProxProg *p, int job, int port, char *s, int sd) { TP_Head H; char *ipstr, *s1, *se; int j, n, rc; size_t L; struct hostent *ptrh; struct sockaddr_in sab; static int jobmap[2] = { TPx_quit, TPx_status }; static cchar *jobname[2] = { "quit", "status" }; j = job - 1; H_init(&H, jobmap[j], 0); ipstr = Loopback; p->fr = p->fw = sd; rc = 1; if (job == status && s) { /* look for ip=... */ if (strncmp(s,"ip=",3) && strncmp(s,"IP=",3)) { fprintf(Stderr, "Expected \"ip=...\", not \"%s\"\n", s); goto ret; } ipstr = s += 3; for(s1 = s; *s1; ) { if (*s1++ == '@') { port = (int)strtol(s1, &se, 10); if (port <= 0 || *se) { fprintf(Stderr, "Bad port value \"%s\" in \"%s\"\n", s1, s-3); goto ret; } L = s1 - s; ipstr = (char*)Malloc1(L); memcpy(ipstr, s, --L); ipstr[L] = 0; break; } } } if (!(ptrh = gethostbyname(ipstr))) { fprintf(Stderr, "Bad IP address \"%s\".\n", ipstr); goto ret; } memset(&sab, 0, sizeof(sab)); sab.sin_family = AF_INET; sab.sin_port = htons((short)port); memcpy(&sab.sin_addr, ptrh->h_addr, ptrh->h_length); if (connect(sd, (struct sockaddr *) &sab, sizeof(sab)) < 0) { fprintf(Stderr, "Not running: could not connect to port %d of \"%s\".\n", port, ipstr); goto ret; } if ((n = p->write(p, &H, sizeof(H))) != sizeof(H)) { fprintf(Stderr, "write to %s@%d failed.\n", ipstr, port); goto ret; } if ((n = p->read(p, &H, sizeof(H))) != sizeof(H) || H.tablen == 0) { fprintf(Stderr, "No reply to %s request.\n", jobname[j]); goto ret; } s1 = (char*)Malloc1(H.tablen+1); if ((n = p->read(p, s1, H.tablen)) <= 0) { fprintf(Stderr, "Incomplete reply to %s request.\n", jobname[j]); goto ret; } s1[n] = 0; printf("%s\n", s1); rc = 0; ret: CloseSocket(p->fr); return rc; } extern int n_badlibs_ASL; static char **my_env; static char * my_getenv(cchar *name) { char **e, *s; size_t L; if (!name) return (char*)my_env; L = strlen(name); for(e = my_env; (s = *e); ++e) { if (!strncmp(s,name,L) && s[L] == '=') return s + L + 1; } return 0; } #ifdef _WIN32 /*{*/ static char* strcp1(char *t, char *f) { while((*t = *f++)) ++t; return t; } static void mswinfork(char **argv) { STARTUPINFO Si; PROCESS_INFORMATION Pi; char *cmdline, *pbuf, pbuf0[2048], *s, *s2; int c, i; size_t L, La, n; L = strlen(progname); La = 0; for(i = 1; (s = argv[i]); ++i) La += strlen(s) + 1; pbuf = pbuf0; if ((n = La + 2*L + 18) > sizeof(pbuf0)) { pbuf = (char*)Malloc(n); if (!pbuf) { fprintf(Stderr, "%s: Malloc failure in mswinfork()\n", progname); exit(1); } } memset(&Si,0,sizeof(Si)); Si.cb = sizeof(Si); Si.wShowWindow = SW_SHOWMINIMIZED; for(s = pbuf, s2 = progname; (c = *s2++); s++) { if (c == '/') c = '\\'; *s = c; } *s = 0; if (s - pbuf > 4 && s[-4] != '.') { strcpy(pbuf+L, ".exe"); L += 4; } cmdline = pbuf + ++L; cmdline[0] = '"'; s = strcp1(cmdline+1, pbuf); s = strcp1(s, "\" --nofork"); for(i = 1; (s2 = argv[i]); ++i) { *s++ = ' '; s = strcp1(s, s2); } c = CreateProcess(pbuf,cmdline,0,0,1,0,0,0,&Si,&Pi); if (!c) { fprintf(Stderr, "%s: CreateProcess(\"%s\") failed in mswinfork()\n", progname, pbuf); exit(1); } CloseHandle(Pi.hThread); CloseHandle(Pi.hProcess); exit(0); } #endif /*}*/ int main(int argc, char **argv, char **arge) { ASL *asl; ProxProg P; Saddr sab, sac; THelp th; Uint *itab; char **av, *s, *se, *stab, *t; fd_set fds; ProxRW Rd, Wr; int dofork, i, job, n, port, proto, rc, rsdf, rstrt, sd, sdi, sdmx; real *rtab; size_t L; socklen_t soclen; struct protoent *ptrp; void (*sighandler)(int); #ifdef _WIN32 /*{{*/ char **av0 = argv, **prs = 0; #define NO_REUSEADDR #else /*}{*/ #ifndef Use_sleep struct timespec wrem, wtime; #endif #endif /*}}*/ /*Sleep(40000);*/ /*DEBUG*/ progname = *argv; my_env = arge; port = 5196; dofork = rc = rsdf = 1; Stderr_init_ASL(); proto = rstrt = 0; sighandler = sighand; nextopt: if ((s = *++argv) && *s == '-') switch(*++s) { case '?': case 'h': return usage(s[1] != 0); case '-': if (!strcmp(++s, "local")) { #ifdef DEBUG_FIFO char dbuf[64]; int dbf, dblen; dblen = snprintf(dbuf, sizeof(dbuf), "echo to debug.fifo to continue /proc/%lu\n", (unsigned long)getpid()); write(2, dbuf, dblen); if ((dbf= open("debug.fifo", O_RDONLY)) >= 0) { read(dbf, dbuf, sizeof(dbuf)); close(dbf); } #endif if (argv[1]) goto baduse; port = 0; s = 0; break; } if (!strcmp(s, "help")) return usage(0); if (!strcmp(s,"version")) return show_ver(); if (!strcmp(s,"nofork")) { dofork = 0; goto nextopt; } if (!*s) { s = *++argv; break; } return usage(1); case 'v': if (!s[1]) return show_ver(); default: baduse: return usage(1); } job = start; if (s) { if (s[0] == 's' && s[1] == 't') { if (!strcmp(s+2,"art")) goto nextarg; else if (!strcmp(s+2, "op")) { job = stop; goto nextarg; } else if (!strcmp(s+2, "atus")) { job = status; nextarg: s = *++argv; } } else if (!strcmp(s, "restart")) { job = rstrt = restart; #ifdef _WIN32 prs = argv; #endif goto nextarg; } } if (s) { if (!strncmp(s,"port=",5)) { port = (int)strtol(s += 5, &se, 10); if (port <= 0 || *se) { fprintf(Stderr, "Bad \"port=\" value \"%s\"\n", s); return usage(1); } s = *++argv; } } i_option_ASL = ""; /* do not load any table handlers */ if (!job && s) { L = 0; for(av = argv; s; s = *++argv) L += strlen(s) + 1; if (L) { i_option_ASL = t = (char*)Malloc(L); while((s = *av++)) { t = strcpe(t, s); t[-1] = '\n'; } t[-1] = 0; } } sd = sdi = 0; memset(&P, 0, sizeof(P)); if (port) { #ifdef _WIN32 if (dofork && !job) mswinfork(av0); if (wsa_startup()) { fprintf(Stderr, "WSAStartup failed.\n"); return 1; } #endif more_restart: Rd = SocRead; Wr = SocWrite; if (!(ptrp = getprotobyname("tcp"))) { fprintf(Stderr, "Can't map TCP to protocol number."); return 1; } proto = ptrp->p_proto; if ((sd = socket(PF_INET, SOCK_STREAM, proto)) < 0) { fprintf(Stderr, "socket(...) failed.\n"); return 1; } sdmx = sd + 1; if (job) { rsdf = dofork; dofork = 0; goto job1; } #ifndef NO_REUSEADDR i = 1; if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i))) fprintf(Stderr, "setsockopt(...) failed.\n"); #endif memset(&sab, 0, sizeof(sab)); sab.sa4.sin_family = AF_INET; sab.sa4.sin_port = htons((short)port); if (bind(sd, (struct sockaddr*)&sab.sa4, sizeof(struct sockaddr))) { perror("bind(...) failed"); goto done; } if (listen(sd, 128)) { fprintf(Stderr, "listen(...) failed\n"); goto done; } FD_ZERO(&fds); if (rstrt) goto more_restart2; } else { dofork = 0; sdmx = 2; Rd = Read; Wr = Write; P.fw = 1; #ifdef _WIN32 P.hr = GetStdHandle(STD_INPUT_HANDLE); P.hw = GetStdHandle(STD_OUTPUT_HANDLE); #endif } job1: P.read = Rd; P.write = Wr; th.P = &P; asl = (ASL*)Malloc(sizeof(ASL)); memset(asl, 0, sizeof(ASL)); asl->p.need_funcadd_ = 1; /* get initial table handlers; we may load more later */ func_add(asl); if (n_badlibs_ASL) goto done; th.ae = asl->i.ae; th.ae->Getenv = my_getenv; th.ae->Tempmem = Temp_mem; #ifdef MDEBUG PRintf = printf; FFlush = fflush; STDout = stdout; #endif th.ae->PrintF = printf_capture; more_restart2: #ifndef _WIN32 /*{*/ signal(SIGCHLD, SIG_IGN); if (dofork) { sighandler = SIG_IGN; if (fork()) _exit(0); setsid(); if (fork()) _exit(0); freopen("/dev/null", "r", stdin); freopen("/dev/null", "w", stdout); freopen("/dev/null", "w", stderr); } signal(SIGHUP, sighandler); signal(SIGQUIT, sighandler); signal(SIGTERM, sighand); #endif /*}*/ if (!port) sighandler = SIG_IGN; signal(SIGINT, sighandler); th.tmi = &TMI; th.at = 0; if (job) { if (rstrt) { dojob(asl->i.ae, &P, stop, port, s, sd); CloseSocket(sd); dofork = rsdf; job = start; #ifdef _WIN32 /*{{*/ Sleep(2000); /* 2 seconds */ #else /*}{*/ #ifdef Use_sleep sleep(2); #else wtime.tv_sec = 2; wtime.tv_nsec = 0; nanosleep(&wtime, &wrem); #endif #endif /*}}*/ printf("Restarting\n"); #ifdef _WIN32 if (dofork) { *prs = "start"; mswinfork(av0); } #endif goto more_restart; } rc = dojob(asl->i.ae, &P, job, port, s, sd); goto done; } for(;;) { if (sigcaught) break; if (port) { FD_SET(sd, &fds); n = select(sdmx, &fds, 0, 0, 0); if (sigcaught) break; if (n <= 0 || !FD_ISSET(sd, &fds)) continue; soclen = sizeof(sac); sdi = accept(sd, &sac.sa, &soclen); if (sdi < 0) { perror("error in accept()"); goto done; } P.fr = P.fw = sdi; } n = Rd(&P, &th.tph, sizeof(TP_Head)); if (n < sizeof(TP_Head)) break; if (strcmp(th.tph.magic, TABLEPROXY_MAGIC)) break; if ((th.needswap = th.tph.arith != Arith_Kind_ASL)) swap(&th.tph.job, sizeof(Uint), TPH_n_Uint); switch(th.tph.job) { case TPx_Read: case TPx_Write: break; case TPx_quit: rc = quitreply(&P, sdi); goto done; case TPx_status: show_status(&P); /* no break */ default: goto loopend1; } rtab = (real*)Temp_mem(th.tmi, th.tph.tablen); itab = (Uint*)(rtab + th.tph.nrtab); stab = (char*)(itab + th.tph.nitab); n = th.tph.tablen; s = (char*)rtab; for(;;) { i = Rd(&P, s, n); if (i <= 0) goto loopend; if ((n -= i) <= 0) break; s += i; } if (th.needswap && stab > (char*)rtab) { swap(rtab, sizeof(real), th.tph.nrtab); swap(itab, sizeof(Uint), th.tph.nitab); } if (th.tph.job == TPx_Read) rtab_read(&th, &P, stab); else wtab_write(&th, &P, rtab, itab, stab); loopend: freeall(&th); loopend1: if (port) CloseSocket(sdi); } done: #ifdef _WIN32 if (port) WSACleanup(); #endif return rc; } #endif /*}} STAND_ALONE*/