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