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 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 167 xassert(int true, char *reason) 168 { 169 if(!true) 170 fatal("assertion failed: %s\n", reason); 171 } 172 173 void * 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 184 xfree(void *p, char *from) 185 { 186 // print("xfree: %lux [%s]\n", p, from); 187 free(p); 188 } 189 190 char * 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* 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 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* 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* 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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* 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* 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* 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* 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* 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* 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 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 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