1 #ifdef WINDOWSNT
2 #include <windows.h>
3 #endif
4 #include <lib9.h>
5 #include <styx.h>
6 #include "styxserver.h"
7 /* #include <winsock.h> */
8
9 #define DEFCOLSIZE 10000
10
11 static int ODebug;
12
13 char Eodbcalloc[] = "no free ODBC handles";
14 char Enoconnect[] = "no ODBC connection";
15
16 static char *netport = "6700";
17 static char *inferno = "inferno";
18
19 Styxserver *iserver;
20
21 /* ----- */
22 #include <sql.h>
23 #include <sqlext.h>
24
25 int nclients = 0;
26
27 typedef struct Env Env;
28 struct Env
29 {
30 SQLHENV h; /* ODBC environment handle */
31 };
32
33 typedef struct Conn Conn;
34 struct Conn
35 {
36 SQLHDBC h; /* ODBC connection handle */
37 int connected;
38 };
39
40 typedef struct Coltype Coltype;
41 struct Coltype
42 {
43 char name[255];
44 ushort type;
45 SQLUINTEGER size;
46 ushort digits;
47 ushort nulls;
48 };
49
50 typedef struct Column Column;
51 struct Column
52 {
53 char *data;
54 SQLINTEGER len;
55 };
56
57 typedef struct Stmt Stmt;
58 struct Stmt
59 {
60 SQLHSTMT h; /* ODBC statement handle */
61 ushort ncols; /* number of columns in result */
62 ulong nrows; /* number of rows affected by update, insert, delete */
63 Coltype *cols; /* column descriptions */
64 Column *rec; /* data record */
65 char *headstr; /* column headings if requested */
66 };
67
68 /* ----- */
69 enum
70 {
71 Qtopdir = 0, /* top level directory */
72 Qnclients,
73 Qprotodir,
74 Qclonus,
75 Qconvdir,
76 Qdata,
77 Qcmd,
78 Qctl,
79 Qstatus,
80 Qformat,
81 Qsources,
82 Qerror,
83
84 MAXPROTO = 1
85 };
86 #define TYPE(x) ((x).path & 0xf)
87 #define CONV(x) (((x).path >> 4)&0xfff)
88 #define PROTO(x) (((x).path >> 16)&0xff)
89 #define QID(p, c, y) (((p)<<16) | ((c)<<4) | (y))
90
91 typedef struct Proto Proto;
92 typedef struct Conv Conv;
93 typedef struct Output Output;
94
95 struct Output {
96 enum {Fixed, Float} style; /* output style */
97 uchar fs; /* float: field separator */
98 uchar rs; /* float: record separator */
99 };
100 Output defoutput = {Float, '|', '\n'};
101
102 struct Conv
103 {
104 int x;
105 int ref;
106 int perm;
107 char *owner;
108 char* state;
109 Proto* p;
110
111 /*-----*/
112 Conn c;
113 Stmt s;
114 Output out;
115 int headings;
116 char errmsg[400]; /* odbc error messages can be big */
117 };
118
119 struct Proto
120 {
121 int x;
122 char *name;
123 uint nc;
124 int maxconv;
125 Conv** conv;
126 Qid qid;
127 };
128
129 typedef struct Dirtab Dirtab;
130 struct Dirtab
131 {
132 char name[255];
133 Qid qid;
134 long length;
135 long perm;
136 };
137
138 static int np;
139 static Proto proto[MAXPROTO];
140 static Conv* protoclone(Proto*, char*);
141
142 typedef int Devgen(Fid*, char *, Dirtab*, int, int, Dir*);
143
144 struct xClient {
145 /* ---- */
146 Env e;
147 };
148
149 #define H(c) ((Env*)(c->u))->h
150
151 void
fatal(char * fmt,...)152 fatal(char *fmt, ...)
153 {
154 char buf[1024], *out;
155 va_list arg;
156 out = vseprint(buf, buf+sizeof(buf), "Fatal error: ", 0);
157 va_start(arg, fmt);
158 out = vseprint(out, buf+sizeof(buf), fmt, arg);
159 va_end(arg);
160 write(2, buf, out-buf);
161 exit(1);
162 }
163
164 #define ASSERT(A,B) xassert((int)A,B)
165
166 void
xassert(int true,char * reason)167 xassert(int true, char *reason)
168 {
169 if(!true)
170 fatal("assertion failed: %s\n", reason);
171 }
172
173 void *
xmalloc(int bytes)174 xmalloc(int bytes)
175 {
176 char *m = malloc(bytes);
177 if(m)
178 memset(m, 0, bytes);
179 // print("xmalloc: %lux (%d)\n", m, bytes);
180 return m;
181 }
182
183 void
xfree(void * p,char * from)184 xfree(void *p, char *from)
185 {
186 // print("xfree: %lux [%s]\n", p, from);
187 free(p);
188 }
189
190 char *
odbcerror(Conv * cv,int lasterr)191 odbcerror(Conv *cv, int lasterr)
192 {
193 char sqlstate[6];
194 long native;
195 char *mp;
196 short msglen;
197
198 if(cv == 0)
199 return "";
200 if(lasterr)
201 return cv->errmsg;
202 if(cv->c.connected)
203 SQLGetDiagRec(SQL_HANDLE_STMT, cv->s.h, 1, sqlstate, &native, cv->errmsg, sizeof(cv->errmsg), &msglen);
204 else
205 SQLGetDiagRec(SQL_HANDLE_DBC, cv->c.h, 1, sqlstate, &native, cv->errmsg, sizeof(cv->errmsg), &msglen);
206 cv->errmsg[msglen]=0;
207 /* fprint(2, "c: sqlstate: %s, msg %s\n", sqlstate, cv->errmsg); */
208 if((mp=strrchr(cv->errmsg, ']')) != 0)
209 return mp+1;
210 return cv->errmsg;
211 }
212
213 char*
odbcsources(Client * c)214 odbcsources(Client *c)
215 {
216 int ss, i;
217 char server[SQL_MAX_DSN_LENGTH+1];
218 char source[1024];
219 char buff[1024+SQL_MAX_DSN_LENGTH+1];
220 short serverlen, sourcelen;
221 char *all = nil, *p;
222
223 for (i=0;; i++) {
224 ss = SQLDataSources(H(c), (i==0 ? SQL_FETCH_FIRST : SQL_FETCH_NEXT),
225 server, sizeof(server), &serverlen,
226 source, sizeof(source), &sourcelen);
227 if (ss != SQL_SUCCESS)
228 break;
229 snprint(buff, sizeof(buff), "%s:%s\n", server, source);
230 if (i == 0)
231 all = strdup(buff);
232 else {
233 p = all;
234 all = malloc(strlen(all)+strlen(buff)+1);
235 strcpy(all, p);
236 strcat(all, buff);
237 free(p);
238 }
239 }
240 return all;
241 }
242
243
244 int
sqlerr(Conv * c,int sqlstatus,char * errp,char * func,char * sqlcall)245 sqlerr(Conv *c, int sqlstatus, char *errp, char *func, char *sqlcall)
246 {
247 char *e;
248
249 errp[0] = 0;
250 e = "failed";
251 if (sqlstatus == SQL_ERROR || sqlstatus == SQL_SUCCESS_WITH_INFO)
252 strecpy(errp, errp+ERRMAX, odbcerror(c, 0));
253 if (sqlstatus == SQL_SUCCESS_WITH_INFO)
254 e = "info";
255 if (sqlstatus != SQL_SUCCESS)
256 fprint(2, "%s: %s %s - %s\n", func, sqlcall, e, errp);
257 if (sqlstatus != SQL_SUCCESS && sqlstatus != SQL_SUCCESS_WITH_INFO)
258 return 1;
259 return 0;
260 }
261
262 char*
odbcnewclient(Client * c)263 odbcnewclient(Client *c)
264 {
265 int ss;
266
267 /* ---- */
268 c->u = styxmalloc(sizeof(Env));
269 ss = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &H(c));
270 if (ss != SQL_SUCCESS && ss != SQL_SUCCESS_WITH_INFO) {
271 fprint(2, "newclient: SQLAllocHandle failed\n");
272 return "SQLAllocHandle failed";
273 }
274 ss = SQLSetEnvAttr(H(c), SQL_ATTR_ODBC_VERSION, (char*)SQL_OV_ODBC2, 0);
275 if (ss != SQL_SUCCESS && ss != SQL_SUCCESS_WITH_INFO) {
276 fprint(2, "newclient: SQLSetEnvAttr failed\n");
277 return "SQLSetEnvAttr failed";
278 }
279 nclients++;
280 return nil;
281 }
282
283 char*
odbcfreeclient(Client * c)284 odbcfreeclient(Client *c)
285 {
286 int ss;
287
288 ss = SQLFreeHandle(SQL_HANDLE_ENV, H(c));
289 if (ss != SQL_SUCCESS && ss != SQL_SUCCESS_WITH_INFO)
290 fprint(2, "freeclient: SQLFreeHandle failed\n");
291 styxfree(c->u);
292 nclients--;
293 return nil;
294 }
295
296 int
parsefields(char * lp,char ** fields,int n,char * sep)297 parsefields(char *lp, char **fields, int n, char *sep)
298 {
299 int i;
300
301 for(i=0; lp && *lp && i<n; i++){
302 while(*lp && strchr(sep, *lp) != 0)
303 *lp++=0;
304 if(*lp == 0)
305 break;
306 fields[i]=lp;
307 while(*lp && strchr(sep, *lp) == 0)
308 lp++;
309 }
310 return i;
311 }
312
313 void
odbcdisconnect(Conv * c)314 odbcdisconnect(Conv *c)
315 {
316 int ss;
317
318 if(c->c.connected){
319 ss = SQLFreeHandle(SQL_HANDLE_STMT, c->s.h);
320 if (ss != SQL_SUCCESS && ss != SQL_SUCCESS_WITH_INFO)
321 fprint(2, "odbcdisconnect: SQLFreeHandle failed\n");
322 ss = SQLDisconnect(c->c.h);
323 if (ss != SQL_SUCCESS && ss != SQL_SUCCESS_WITH_INFO)
324 fprint(2, "odbcdisconnect: SQLDisconnect failed\n");
325 c->c.connected = 0;
326 }
327 }
328
329 int
odbcconnect(Conv * c,char * server,char * user,char * auth,char * ename)330 odbcconnect(Conv *c, char *server, char *user, char *auth, char *ename)
331 {
332 int ss;
333
334 odbcdisconnect(c);
335 ss = SQLConnect(c->c.h, server, SQL_NTS, user, strlen(user), auth, strlen(auth));
336 if (sqlerr(c, ss, ename, "odbcconnect", "SQLConnect"))
337 return -1;
338 c->c.connected = 1;
339 ss = SQLAllocHandle(SQL_HANDLE_STMT, c->c.h, &c->s.h);
340 if (sqlerr(c, ss, ename, "odbcconnect", "SQLAllocHandle"))
341 return -1;
342 return 0;
343 }
344
345 int
odbcnewconv(Client * c,Conv * cv)346 odbcnewconv(Client *c, Conv *cv)
347 {
348 int ss;
349
350 ss = SQLAllocHandle(SQL_HANDLE_DBC, H(c), &cv->c.h);
351 if (ss != SQL_SUCCESS && ss != SQL_SUCCESS_WITH_INFO) {
352 fprint(2, "odbcnewconv: SQLAllocHandle failed\n");
353 return -1;
354 }
355 return 0;
356 }
357
358 void
odbcfreeconv(Conv * c)359 odbcfreeconv(Conv *c)
360 {
361 int ss;
362
363 odbcdisconnect(c);
364 ss = SQLFreeHandle(SQL_HANDLE_DBC, c->c.h);
365 if (ss != SQL_SUCCESS && ss != SQL_SUCCESS_WITH_INFO)
366 fprint(2, "odbcfreeconv: SQLFreeHandle failed\n");
367 }
368
369 /* Free up all memory used in the last query */
370 void
freestat(Stmt * s)371 freestat(Stmt *s)
372 {
373 int i;
374 if (s->ncols == 0)
375 return;
376 for(i=0; i<s->ncols; i++){
377 Column *d = &s->rec[i];
378 xfree(d->data, "freestat - data");
379 }
380 xfree(s->cols, "freestat - cols");
381 s->cols = 0;
382 xfree(s->rec, "freestat - rec");
383 s->rec = 0;
384 xfree(s->headstr, "freestat - headstr");
385 s->headstr = 0;
386 s->ncols = 0;
387 }
388
389
390 /* build an array describing the columns */
391 int
mkcols(Conv * c,char * ename)392 mkcols(Conv *c, char *ename)
393 {
394 Stmt *s;
395 int rv, i, err, hsize;
396 ushort ignore;
397 char *p;
398
399 s = &c->s;
400 s->ncols = 0;
401 rv = SQLNumResultCols(s->h, &s->ncols);
402 if (sqlerr(c, rv, ename, "mkcols", "SQLNumResultCols"))
403 return -1;
404 s->cols = xmalloc(s->ncols*sizeof(Coltype));
405 err = 0;
406 hsize = 0;
407 for(i=0; i<s->ncols; i++){
408 Coltype *t = &s->cols[i];
409 rv = SQLDescribeCol(s->h, i+1, t->name, sizeof(t->name), &ignore, &t->type, &t->size, &t->digits, &t->nulls);
410 if (sqlerr(c, rv, ename, "mkcols", "SQLDescribeCol"))
411 err++;
412 if(t->size == 0 || t->size > MSGMAX) /* odbc should return 0 if size not available, not -1 */
413 t->size = DEFCOLSIZE;
414 hsize += strlen(t->name) + 1;
415 }
416 if (c->headings) {
417 hsize += 2;
418 s->headstr = xmalloc(hsize);
419 p = s->headstr;
420 for(i=0; i<s->ncols; i++) {
421 Coltype *t = &s->cols[i];
422 p += sprint(p, "%s%c", t->name, c->out.fs);
423 }
424 p[-1] = c->out.rs;
425 } else
426 s->headstr = 0;
427 return (err ? -1 : 0);
428 }
429
430 /* build a record to hold `fetched' results */
431 int
mkrec(Conv * c,char * ename)432 mkrec(Conv *c, char *ename)
433 {
434 Stmt *s;
435 int rv, i;
436
437 s = &c->s;
438 s->rec = xmalloc(s->ncols*sizeof(Column));
439 for(i=0; i<s->ncols; i++){
440 Coltype *t = &s->cols[i];
441 Column *d = &s->rec[i];
442 if (ODebug)
443 print("Column %d size=%ud type=%hd\n", i, t->size, t->type);
444 d->data = xmalloc(t->size+1); /* expects to zero terminate */
445 rv = SQLBindCol(s->h, i+1, SQL_C_CHAR, d->data, t->size+1, &d->len);
446 if (sqlerr(c, rv, ename, "mkrec", "SQLBindCol"))
447 return -1;
448 }
449 return 0;
450 }
451
452 int
rowcount(Conv * c,char * ename)453 rowcount(Conv *c, char *ename)
454 {
455 Stmt *s;
456 int rv;
457
458 s = &c->s;
459 s->nrows = 0;
460 rv = SQLRowCount(s->h, &s->nrows);
461 if (sqlerr(c, rv, ename, "rowcount", "SQLRowCount"))
462 return -1;
463 return 0;
464 }
465
466 int
odbcfetch(Conv * c,char * ename)467 odbcfetch(Conv *c, char *ename)
468 {
469 Stmt *s = &c->s;
470 int rv;
471
472 rv = SQLFetch(s->h);
473 if(rv == SQL_NO_DATA) {
474 freestat(s);
475 return 0;
476 }
477 if (sqlerr(c, rv, ename, "odbcfetch", "SQLFetch")) {
478 freestat(s);
479 return -1;
480 }
481 return 1;
482 }
483
484 int
odbcresults(Conv * c,char * ename)485 odbcresults(Conv *c, char *ename)
486 {
487 if(mkcols(c, ename))
488 return -1;
489 if(mkrec(c, ename))
490 return -1;
491 if(rowcount(c, ename))
492 return -1;
493 return 0;
494 }
495
496 void
struncate(char * s,long * len)497 struncate(char *s, long *len)
498 {
499 long i;
500 for (i=0; i<*len; i++)
501 if (s[i] == 0) {
502 *len = i;
503 return;
504 }
505 }
506
507 void
fix_delim(char * p,int len,char delim)508 fix_delim(char *p, int len, char delim)
509 {
510 int i;
511 for (i=0; i<len; i++)
512 if (p[i] == delim)
513 p[i] = '\\';
514 }
515
516 long
odbcdataread(Conv * c,void * a,long n,ulong offset,char * ename)517 odbcdataread(Conv *c, void *a, long n, ulong offset, char *ename)
518 {
519 Stmt *s = &c->s;
520 int i, r;
521 long left;
522 char *p, *lastp;
523
524 if(c->c.connected == 0){
525 strcpy(ename, Enoconnect);
526 return -1;
527 }
528 if (s->cols == 0 || s->rec == 0)
529 return 0;
530 p = a;
531 left = n;
532 if (c->headings) {
533 r = strlen(s->headstr);
534 if (r && offset < r) {
535 memcpy(p, s->headstr+offset, r-offset);
536 p += r-offset;
537 left -= r-offset;
538 return n-left;
539 }
540 }
541 if((r=odbcfetch(c, ename)) < 0)
542 return -1;
543 if(r == 0)
544 return 0;
545 for(i=0; i<s->ncols; i++){
546 Coltype *t = &s->cols[i];
547 Column *d = &s->rec[i];
548 if (ODebug)
549 fprint(2, "Col %d Returned data len=%d\n", i, d->len);
550 if(d->len <= 0) /* SQL_NULL_DATA or strange error! */
551 d->len = 0;
552 if (d->len > t->size+1)
553 d->len = t->size+1;
554 if(left <= d->len+1) /* whole fields */
555 break;
556 struncate(d->data, &d->len); /* assume string data and stop on an embedded null */
557 memcpy(p, d->data, d->len);
558 lastp = p;
559 left -= d->len;
560 p += d->len;
561 fix_delim(lastp, d->len, '\n');
562 switch(c->out.style){
563 case Float:
564 fix_delim(lastp, d->len, c->out.fs);
565 *p++ = (i==s->ncols-1)? c->out.rs: c->out.fs;
566 left--;
567 break;
568 case Fixed:
569 r = t->size - d->len;
570 if(r < 0)
571 r = 0;
572 if(left < r)
573 r = left;
574 memset(p, ' ', r);
575 left -= r;
576 p += r;
577 break;
578 }
579 }
580 if (left < 0)
581 fprint(2, "*** left<0 n=%d left=%d\n", n, left);
582 return n-left;
583 }
584
585 /*
586 * Returns a description of the format of a fixed width output
587 * record. `start' is the offset of the first character in the field.
588 * `end' is one greater than the offset of the last character of the field.
589 * `name' is the column name (which may contain spaces).
590 * `start' and `end' are terminated with a space, `name' with a newline.
591 * return 1 record containing one line for each field in the output:
592 * start1 end1 name1\n
593 * start2 end2 name2\n
594 * ....
595 */
596 long
odbcfmtread(Conv * c,void * a,long n,ulong offset,char * ename)597 odbcfmtread(Conv *c, void *a, long n, ulong offset, char *ename)
598 {
599 Stmt *s = &c->s;
600 int i, len;
601 long left, off;
602 char *p;
603 char buf[100];
604
605 if(offset > 0)
606 return 0;
607 p = a;
608 left = n;
609 off = 0;
610 for(i=0; i<s->ncols; i++){
611 Coltype *t = &s->cols[i];
612
613 len = snprint(buf, sizeof(buf), "%ld %ld %s\n", off, off+t->size, t->name);
614 off += t->size;
615 if(left < len)
616 break;
617 memcpy(p, buf, len);
618 left -= len;
619 p += len;
620
621 }
622 return n-left;
623 }
624
625 int
odbctables(Conv * c,char * ename)626 odbctables(Conv *c, char *ename)
627 {
628 int rv;
629
630 if(c->c.connected == 0){
631 strcpy(ename, Enoconnect);
632 return -1;
633 }
634 rv = SQLCloseCursor(c->s.h);
635 rv = SQLTables(c->s.h, 0, 0, 0, 0, 0, 0, 0, 0);
636 if (sqlerr(c, rv, ename, "odbctables", "SQLTables"))
637 return -1;
638 if(odbcresults(c, ename))
639 return -1;
640 return 0;
641 }
642
643 int
odbccolumns(Conv * c,char * table,char * ename)644 odbccolumns(Conv *c, char *table, char *ename)
645 {
646 int rv;
647
648 if(c->c.connected == 0){
649 strcpy(ename, Enoconnect);
650 return -1;
651 }
652 rv = SQLCloseCursor(c->s.h);
653 rv = SQLColumns(c->s.h, 0, 0, 0, 0, table, strlen(table), 0, 0);
654 if (sqlerr(c, rv, ename, "odbccolumns", "SQLColumns"))
655 return -1;
656 if(odbcresults(c, ename))
657 return -1;
658 return 0;
659 }
660
661 int
odbcexec(Conv * c,char * cmd,int cmdlen,char * ename)662 odbcexec(Conv *c, char *cmd, int cmdlen, char *ename)
663 {
664 int rv;
665
666 if(c->c.connected == 0){
667 strcpy(ename, Enoconnect);
668 return -1;
669 }
670 SQLCloseCursor(c->s.h);
671 rv = SQLExecDirect(c->s.h, cmd, cmdlen);
672 if (sqlerr(c, rv, ename, "odbcexec", "SQLExecDirect"))
673 return -1;
674 if(odbcresults(c, ename))
675 return -1;
676 return 0;
677 }
678
679 int
odbctrans(Conv * c,char * cmd,char * ename)680 odbctrans(Conv *c, char *cmd, char *ename)
681 {
682 int rv;
683
684 if(strcmp(cmd, "auto") == 0){
685 rv = SQLSetConnectAttr(c->c.h, SQL_ATTR_AUTOCOMMIT, (char*)SQL_AUTOCOMMIT_ON, 0);
686 } else if(strcmp(cmd, "begin") == 0){
687 rv = SQLSetConnectAttr(c->c.h, SQL_ATTR_AUTOCOMMIT, (char*)SQL_AUTOCOMMIT_OFF, 0);
688 } else if(strcmp(cmd, "commit") == 0){
689 rv = SQLEndTran(SQL_HANDLE_DBC, c->c.h, SQL_COMMIT);
690 } else if(strcmp(cmd, "rollback") == 0){
691 rv = SQLEndTran(SQL_HANDLE_DBC, c->c.h, SQL_ROLLBACK);
692 } else {
693 strcpy(ename, Ebadarg);
694 return -1;
695 }
696 if (sqlerr(c, rv, ename, "odbctrans", "SQLSetConnectAttr/SQLEndTran"))
697 return -1;
698 return 0;
699 }
700
701 int
readstr(ulong off,char * buf,ulong n,char * str)702 readstr(ulong off, char *buf, ulong n, char *str)
703 {
704 int size;
705
706 size = strlen(str);
707 if(off >= size)
708 return 0;
709 if(off+n > size)
710 n = size-off;
711 memmove(buf, str+off, n);
712 return n;
713 }
714
715 static void
newproto(char * name,int maxconv)716 newproto(char *name, int maxconv)
717 {
718 int l;
719 Proto *p;
720
721 if(np >= MAXPROTO) {
722 print("no %s: increase MAXPROTO", name);
723 return;
724 }
725
726 p = &proto[np];
727 p->name = strdup(name);
728 p->qid.path = QID(np, 0, Qprotodir);
729 p->qid.type = QTDIR;
730 p->x = np++;
731 p->maxconv = maxconv;
732 l = sizeof(Conv*)*(p->maxconv+1);
733 p->conv = xmalloc(l);
734 if(p->conv == 0)
735 fatal("no memory");
736 memset(p->conv, 0, l);
737 }
738
739 char*
openmode(int * o)740 openmode(int *o)
741 {
742 if(*o >= (OTRUNC|OCEXEC|ORCLOSE|OEXEC)){
743 return Ebadarg;
744 }
745 *o &= ~(OTRUNC|OCEXEC|ORCLOSE);
746 if(*o > OEXEC){
747 return Ebadarg;
748 }
749 if(*o == OEXEC)
750 *o = OREAD;
751 return nil;
752 }
753
754 static Conv*
protoclone(Proto * p,char * user)755 protoclone(Proto *p, char *user)
756 {
757 Conv *c, **pp, **ep;
758 uvlong nr;
759 char buf[16];
760
761 c = 0;
762 ep = &p->conv[p->maxconv];
763 for(pp = p->conv; pp < ep; pp++) {
764 c = *pp;
765 if(c == 0) {
766 c = xmalloc(sizeof(Conv));
767 if(c == 0)
768 return 0;
769 c->ref = 1;
770 c->p = p;
771 c->x = pp - p->conv;
772 p->nc++;
773 *pp = c;
774 break;
775 }
776 if(c->ref == 0) {
777 c->ref++;
778 break;
779 }
780 }
781 if(pp >= ep)
782 return 0;
783
784 c->owner = strdup(user);
785 c->perm = 0660;
786 c->state = "Open";
787 c->out = defoutput;
788 c->headings = 0;
789 c->errmsg[0] = 0;
790
791 nr = QID(0, c->x, Qconvdir);
792 snprint(buf, sizeof(buf), "%d", c->x);
793 styxadddir(iserver, Qprotodir, nr, buf, 0555, c->owner);
794 styxaddfile(iserver, nr, QID(0, c->x, Qcmd), "cmd", c->perm, c->owner);
795 styxaddfile(iserver, nr, QID(0, c->x, Qctl), "ctl", c->perm, c->owner);
796 styxaddfile(iserver, nr, QID(0, c->x, Qdata), "data", c->perm, c->owner);
797 styxaddfile(iserver, nr, QID(0, c->x, Qerror), "error", c->perm, c->owner);
798 styxaddfile(iserver, nr, QID(0, c->x, Qformat), "format", c->perm, c->owner);
799 styxaddfile(iserver, nr, QID(0, c->x, Qsources), "sources", c->perm, c->owner);
800 styxaddfile(iserver, nr, QID(0, c->x, Qstatus), "status", 0444, c->owner);
801
802 return c;
803 }
804
805 char*
dbopen(Qid * qid,int omode)806 dbopen(Qid *qid, int omode)
807 {
808 Proto *p;
809 int perm;
810 Conv *cv;
811 char *user;
812 Qid q;
813 Client *c;
814
815 q = *qid;
816 c = styxclient(iserver);
817
818 perm = 0;
819 omode &= 3;
820 switch(omode) {
821 case OREAD:
822 perm = 4;
823 break;
824 case OWRITE:
825 perm = 2;
826 break;
827 case ORDWR:
828 perm = 6;
829 break;
830 }
831
832 switch(TYPE(q)) {
833 default:
834 break;
835 case Qtopdir:
836 case Qprotodir:
837 case Qconvdir:
838 case Qstatus:
839 case Qformat:
840 case Qsources:
841 case Qerror:
842 if(omode != OREAD){
843 return Eperm;
844 }
845 break;
846 case Qclonus:
847 p = &proto[PROTO(q)];
848 cv = protoclone(p, c->uname);
849 if(cv == 0){
850 return Enodev;
851 }
852 qid->path = QID(p->x, cv->x, Qctl);
853 qid->type = 0;
854 qid->vers = 0;
855 if(odbcnewconv(c, cv) != 0){
856 return Eodbcalloc;
857 }
858 break;
859 case Qdata:
860 case Qcmd:
861 case Qctl:
862 p = &proto[PROTO(q)];
863 cv = p->conv[CONV(q)];
864 user = c->uname;
865 if((perm & (cv->perm>>6)) != perm) {
866 if(strcmp(user, cv->owner) != 0 ||
867 (perm & cv->perm) != perm) {
868 return Eperm;
869 }
870 }
871 cv->ref++;
872 if(cv->ref == 1) {
873 cv->state = "Open";
874 cv->owner = strdup(user);
875 cv->perm = 0660;
876 if(odbcnewconv(c, cv) != 0){
877 return Eodbcalloc;
878 }
879 }
880 break;
881 }
882 return openmode(&omode);
883 }
884
885 char*
dbclose(Qid qid,int mode)886 dbclose(Qid qid, int mode)
887 {
888 Conv *cc;
889
890 USED(mode);
891 switch(TYPE(qid)) {
892 case Qctl:
893 case Qcmd:
894 case Qdata:
895 cc = proto[PROTO(qid)].conv[CONV(qid)];
896 if(--cc->ref != 0)
897 break;
898 cc->owner = inferno;
899 cc->perm = 0666;
900 cc->state = "Closed";
901 odbcfreeconv(cc);
902 styxrmfile(iserver, QID(0, cc->x, Qconvdir));
903 break;
904 }
905 return nil;
906 }
907
908 static char ebuf[ERRMAX];
909
910 char*
dbread(Qid qid,char * ba,ulong * n,vlong offset)911 dbread(Qid qid, char *ba, ulong *n, vlong offset)
912 {
913 uchar *a = ba;
914 Conv *c;
915 Proto *x;
916 char buf[128], *p, *s;
917 long r;
918 ulong m;
919
920 m = *n;
921 ebuf[0] = 0;
922 p = a;
923 switch(TYPE(qid)) {
924 default:
925 return Eperm;
926 case Qnclients:
927 snprint(buf, sizeof(buf), "%d\n", nclients);
928 *n = readstr(offset, p, m, buf);
929 return nil;
930 case Qprotodir:
931 case Qtopdir:
932 case Qconvdir:
933 return "bad read of directory";
934 case Qctl:
935 snprint(buf, sizeof(buf), "%ld", CONV(qid));
936 *n = readstr(offset, p, m, buf);
937 return nil;
938 case Qstatus:
939 x = &proto[PROTO(qid)];
940 c = x->conv[CONV(qid)];
941 snprint(buf, sizeof(buf), "%s/%d %ld %s %s\n",
942 c->p->name, c->x, c->ref, c->state, "");
943 *n = readstr(offset, p, m, buf);
944 return nil;
945 case Qdata:
946 c = proto[PROTO(qid)].conv[CONV(qid)];
947 *n = odbcdataread(c, a, m, offset, ebuf);
948 if(ebuf[0] != 0)
949 return ebuf;
950 return nil;
951 case Qformat:
952 c = proto[PROTO(qid)].conv[CONV(qid)];
953 *n = odbcfmtread(c, a, m, offset, ebuf);
954 if(ebuf[0] != 0)
955 return ebuf;
956 return nil;
957 case Qerror:
958 c = proto[PROTO(qid)].conv[CONV(qid)];
959 *n = readstr(offset, p, m, odbcerror(c, 1));
960 return nil;
961 case Qsources:
962 c = proto[PROTO(qid)].conv[CONV(qid)];
963 s = odbcsources(styxclient(iserver));
964 r = readstr(offset, p, m, s);
965 free(s);
966 *n = r;
967 return nil;
968 }
969 return nil;
970 }
971
972 char*
dbwrite(Qid qid,char * ba,ulong * n,vlong offset)973 dbwrite(Qid qid, char *ba, ulong *n, vlong offset)
974 {
975 uchar *a = ba;
976 int nf;
977 Conv *c;
978 Proto *x;
979 char *fields[10], buf[512], safebuf[512];
980 ulong m;
981
982 m = *n;
983 ebuf[0] = 0;
984 switch(TYPE(qid)) {
985 default:
986 return Eperm;
987 case Qctl:
988 x = &proto[PROTO(qid)];
989 c = x->conv[CONV(qid)];
990 //
991 if(m > sizeof(buf)-1)
992 m = sizeof(buf)-1;
993 memmove(buf, a, m);
994 buf[m] = '\0';
995 if (ODebug)
996 fprint(2, "write Qctl: <%s>\n", buf);
997 fields[0] = 0;
998 nf = parsefields(buf, fields, sizeof(fields)/sizeof(*fields), " \n\t");
999 if (nf == 0) {
1000 return Ebadarg;
1001 }
1002 if(strcmp(fields[0], "connect") == 0){ /* connect database [user!auth] */
1003 char *afields[2];
1004 char *user = "";
1005 char *auth = "";
1006 switch(nf){
1007 default:
1008 return Ebadarg;
1009 case 2:
1010 break;
1011 case 3:
1012 nf = parsefields(fields[2], afields, 2, "!");
1013 switch(nf){
1014 case 2:
1015 user = afields[0];
1016 auth = afields[1];
1017 break;
1018 case 1:
1019 if(fields[2][0] == 0)
1020 auth = afields[0];
1021 else
1022 user = afields[0];
1023 break;
1024 default:
1025 break;
1026 }
1027 break;
1028 }
1029 if(odbcconnect(c, fields[1], user, auth, ebuf) < 0)
1030 return ebuf;
1031 c->state = "Connected";
1032 } else if(strcmp(fields[0], "disconnect") == 0){
1033 odbcdisconnect(c);
1034 c->state = "Disconnected";
1035 } else if(strcmp(fields[0], "fixed") == 0){
1036 c->out = defoutput;
1037 c->out.style = Fixed;
1038 } else if(strcmp(fields[0], "float") == 0){
1039 c->out = defoutput;
1040 c->out.style = Float;
1041 if(nf > 1)
1042 c->out.fs = fields[1][0];
1043 if(nf > 2)
1044 c->out.rs = fields[2][0];
1045 } else if(strcmp(fields[0], "headings") == 0){
1046 c->headings = 1;
1047 } else if(strcmp(fields[0], "noheadings") == 0){
1048 c->headings = 0;
1049 } else if(strcmp(fields[0], "trans") == 0){ /* begin, auto, commit, rollback */
1050 if(nf < 2){
1051 return Ebadarg;
1052 }
1053 if(odbctrans(c, fields[1], ebuf) < 0)
1054 return ebuf;
1055 } else {
1056 return Ebadcmd;
1057 }
1058 *n = m;
1059 return nil;
1060 case Qcmd:
1061 x = &proto[PROTO(qid)];
1062 c = x->conv[CONV(qid)];
1063 if(m > sizeof(buf)-1)
1064 m = sizeof(buf)-1;
1065 memmove(buf, a, m);
1066 buf[m] = '\0';
1067 if (ODebug)
1068 fprint(2, "write Qcmd: <%s>\n", buf);
1069 memmove(safebuf, a, m);
1070 safebuf[m] = '\0';
1071 fields[0] = 0;
1072 nf = parsefields(buf, fields, 3, " \n\t");
1073 if (nf == 0) {
1074 return Ebadarg;
1075 }
1076 if(strcmp(fields[0], "tables") == 0){
1077 if(odbctables(c, ebuf))
1078 return ebuf;
1079 }else if(strcmp(fields[0], "columns") == 0){
1080 if(nf < 2){
1081 return Ebadarg;
1082 }
1083 if(odbccolumns(c, &safebuf[strlen(fields[0])+1], ebuf)) /* allow for spaces in table name */
1084 return ebuf;
1085 } else
1086 if (odbcexec(c, a, m, ebuf))
1087 return ebuf;
1088 *n = m;
1089 return nil;
1090 case Qdata:
1091 return Eperm;
1092 }
1093 return nil;
1094 }
1095
1096 void
badusage(void)1097 badusage(void)
1098 {
1099 fprint(2, "Usage: odbc [-d] [-p port]\n");
1100 exit(1);
1101 }
1102
1103 Styxops ops = {
1104 odbcnewclient, /* newclient */
1105 odbcfreeclient, /* freeclient */
1106
1107 nil, /* attach */
1108 nil, /* walk */
1109 dbopen, /* open */
1110 nil, /* create */
1111 dbread, /* read */
1112 dbwrite, /* write */
1113 dbclose, /* close */
1114 nil, /* remove */
1115 nil, /* stat */
1116 nil, /* wstat */
1117 };
1118
1119 void
main(int argc,char * argv[])1120 main(int argc, char *argv[])
1121 {
1122 Styxserver s;
1123
1124 ARGBEGIN {
1125 default:
1126 badusage();
1127 case 'd': /* Debug */
1128 ODebug = 1;
1129 styxdebug();
1130 break;
1131 case 'p': /* Debug */
1132 netport = EARGF(badusage());
1133 break;
1134 } ARGEND
1135
1136 iserver = &s;
1137 styxinit(&s, &ops, netport, -1, 1);
1138 styxaddfile(&s, Qroot, Qnclients, "nclients", 0444, inferno);
1139 styxadddir(&s, Qroot, Qprotodir, "db", 0555, inferno);
1140 styxaddfile(&s, Qprotodir, Qclonus, "new", 0666, inferno);
1141 newproto("db", 100);
1142 for (;;) {
1143 styxwait(&s);
1144 styxprocess(&s);
1145 }
1146 styxend(&s);
1147 }
1148