xref: /inferno-os/tools/odbc/odbc.c (revision ea0789e70d92227d8f4be656b205102f82f00b97)
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