00001
00002
00003
00004
00005
00006
00007
00008
00009 #include "copyright.h"
00010 #include "config.h"
00011
00012 #include <stdio.h>
00013 #include <stdlib.h>
00014 #include <unistd.h>
00015 #include <string.h>
00016 #include <sys/types.h>
00017 #include <sys/stat.h>
00018 #include <dbi/dbi.h>
00019 #include <sys/file.h>
00020 #include <sys/ioctl.h>
00021 #include <sys/wait.h>
00022 #include <signal.h>
00023 #include <errno.h>
00024
00025 #include "externs.h"
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038 #ifdef DEBUG_SQL
00039 #ifndef DEBUG
00040 #define DEBUG
00041 #endif
00042 #endif
00043 #include "debug.h"
00044
00045 #ifdef SQL_SUPPORT
00046 #define MAX_QUERIES 8
00047
00048 static int running_queries = 0;
00049 dbi_conn conn = NULL;
00050
00051 #define DBIS_EFAIL -1
00052 #define DBIS_READY 0
00053 #define DBIS_RESOURCE 1
00054
00055 static int dbi_initialized = 0;
00056 static int dbi_state;
00057 static int query_counter = 0;
00058 static int recent = 0;
00059
00060 static struct query_state_t {
00061 dbref thing;
00062 int attr;
00063 char *preserve;
00064 char *query;
00065 struct event ev;
00066 char *rdelim;
00067 char *cdelim;
00068 struct query_state_t *next;
00069 int serial;
00070 struct timeval start;
00071 char slot;
00072 int fd;
00073 int pid;
00074 } *running = NULL, *pending = NULL, *pending_tail = NULL, *recent_head =
00075 NULL, *recent_tail = NULL;
00076
00077 static struct timeval query_timeout = { 10, 0 };
00078
00079 void sqlchild_init();
00080 void sqlchild_destruct();
00081 static void sqlchild_kill_all();
00082 int sqlchild_kill(int requestId);
00083 void sqlchild_list(dbref thing);
00084 static void sqlchild_kill_query(struct query_state_t *aqt);
00085 static void sqlchild_child_abort_query(struct query_state_t *aqt,
00086 char *error);
00087 static void sqlchild_child_abort_query_dbi(struct query_state_t *aqt,
00088 char *error);
00089 static void sqlchild_child_execute_query(struct query_state_t *aqt);
00090 static void sqlchild_finish_query(int fd, short events, void *arg);
00091 static void sqlchild_check_queue();
00092 int sqlchild_request(dbref thing, int attr, char slot, char *pres,
00093 char *query, char *rdelim, char *cdelim);
00094
00095 void sqlchild_init()
00096 {
00097 dbi_driver *driver;
00098
00099 if(dbi_initialized)
00100 return;
00101
00102 dprintk("initializing sqlchild.");
00103
00104 if(dbi_initialize(NULL) == -1) {
00105 dprintk("dbi_initialized() failed.");
00106 dbi_state = DBIS_EFAIL;
00107 return;
00108 }
00109
00110 dprintk("libdbi started.");
00111
00112 driver = dbi_driver_list(NULL);
00113 while (driver != NULL) {
00114 dprintk("libdbi driver '%s' ready.", dbi_driver_get_name(driver));
00115 driver = dbi_driver_list(driver);
00116 }
00117
00118 dbi_state = DBIS_READY;
00119 }
00120
00121 void sqlchild_destruct()
00122 {
00123 dprintk("shutting down.");
00124 dbi_state = DBIS_EFAIL;
00125 sqlchild_kill_all();
00126 dbi_shutdown();
00127 }
00128
00129
00130 int sqlchild_request(dbref thing, int attr, char slot, char *pres,
00131 char *query, char *rdelim, char *cdelim)
00132 {
00133 int fds[2];
00134 struct query_state_t *aqt;
00135
00136 if(dbi_state < DBIS_READY)
00137 return -1;
00138
00139 aqt = malloc(sizeof(struct query_state_t));
00140 aqt->thing = thing;
00141 aqt->attr = attr;
00142 aqt->preserve = strdup(pres);
00143 aqt->query = strdup(query);
00144 aqt->rdelim = strdup(rdelim);
00145 aqt->cdelim = strdup(cdelim);
00146 aqt->serial = query_counter++;
00147 aqt->slot = slot;
00148
00149 if(pending == NULL) {
00150 aqt->next = NULL;
00151 pending = aqt;
00152 pending_tail = aqt;
00153 } else {
00154 pending_tail->next = aqt;
00155 aqt->next = NULL;
00156 pending_tail = aqt;
00157 }
00158 sqlchild_check_queue();
00159 return 1;
00160 }
00161
00162 void sqlchild_kill_all()
00163 {
00164 dprintk("shutting down.\n");
00165 if(!dbi_initialized)
00166 return;
00167 while (running) {
00168 sqlchild_kill_query(running);
00169 }
00170 }
00171
00172 int sqlchild_kill(int requestId)
00173 {
00174 int status;
00175 struct query_state_t *iter = NULL, *aqt = NULL;
00176
00177 dprintk("received request to terminate %d", requestId);
00178
00179 if(running)
00180 iter = running;
00181 while (iter)
00182 if(iter->serial == requestId) {
00183 sqlchild_kill_query(iter);
00184 return 1;
00185 } else
00186 iter = iter->next;
00187 if(pending)
00188 iter = pending;
00189 while (iter)
00190 if(iter->serial == requestId) {
00191 sqlchild_kill_query(iter);
00192 return 1;
00193 } else
00194 iter = iter->next;
00195 return 0;
00196 }
00197
00198 static void sqlchild_kill_query(struct query_state_t *aqt)
00199 {
00200 struct query_state_t *iter;
00201
00202 dprintk("terminating query %d", aqt->serial);
00203
00204 kill(aqt->pid, SIGTERM);
00205 waitpid(aqt->pid, NULL, 0);
00206
00207 iter = running;
00208 if(running == aqt) {
00209 running = aqt->next;
00210 } else {
00211 while (iter) {
00212 if(iter->next == aqt) {
00213 iter->next = aqt->next;
00214 break;
00215 }
00216 iter = iter->next;
00217 }
00218 }
00219 if(aqt->preserve)
00220 free(aqt->preserve);
00221 if(aqt->query)
00222 free(aqt->query);
00223 if(aqt->rdelim)
00224 free(aqt->rdelim);
00225 if(aqt->cdelim)
00226 free(aqt->cdelim);
00227 close(aqt->fd);
00228 free(aqt);
00229 running_queries--;
00230 sqlchild_check_queue();
00231 };
00232
00233 void sqlchild_list(dbref thing)
00234 {
00235 int nactive = 0, npending = 0;
00236 int nrecent = recent;
00237 struct query_state_t *aqt;
00238 notify(thing, "/--------------------------- Recent Queries");
00239 if(recent) {
00240 aqt = recent_head;
00241 while (aqt) {
00242 notify_printf(thing, "%08d #%8d %40s", aqt->serial, aqt->thing,
00243 aqt->query);
00244 aqt = aqt->next;
00245 }
00246 }
00247 notify(thing, "/--------------------------- Running Queries");
00248 if(running) {
00249 aqt = running;
00250 while (aqt) {
00251 notify_printf(thing, "%08d #%8d %40s", aqt->serial, aqt->thing,
00252 aqt->query);
00253 aqt = aqt->next;
00254 nactive++;
00255 }
00256 } else {
00257 notify(thing, "- No active queries.");
00258 }
00259 notify(thing, "/--------------------------- Pending Queries");
00260 if(pending) {
00261 aqt = pending;
00262 while (aqt) {
00263 notify_printf(thing, "%08d #%-8d %40s", aqt->serial, aqt->thing,
00264 aqt->query);
00265 aqt = aqt->next;
00266 npending++;
00267 }
00268 } else {
00269 notify(thing, "- No pending queries.");
00270 }
00271 notify_printf(thing, "%d active and %d pending queries.", nactive,
00272 pending);
00273 }
00274
00275 struct query_response {
00276 int status;
00277 int n_chars;
00278 };
00279
00280
00281
00282 static void sqlchild_finish_query(int fd, short events, void *arg)
00283 {
00284 pid_t pchild;
00285 int status;
00286 char *argv[5];
00287 struct query_state_t *aqt = (struct query_state_t *) arg, *iter;
00288 struct query_response resp = { -1, 0 };
00289 char buffer[LBUF_SIZE];
00290 buffer[0] = '\0';
00291
00292
00293
00294 if(read(aqt->fd, &resp, sizeof(struct query_response)) < 0) {
00295 log_perror("SQL", "FAIL", NULL, "sqlchild_finish_query");
00296 argv[0] = "-1";
00297 argv[1] = "";
00298 argv[3] = "serious braindamage";
00299 goto fail;
00300
00301 }
00302
00303 if(resp.n_chars >= LBUF_SIZE) {
00304 resp.n_chars = LBUF_SIZE - 1;
00305 }
00306
00307 if(resp.n_chars) {
00308 if(read(aqt->fd, buffer, resp.n_chars) < 0) {
00309 log_perror("SQL", "FAIL", NULL, "sqlchild_finish_query");
00310 argv[0] = "-1";
00311 argv[1] = "";
00312 argv[3] = "serious braindamage";
00313 goto fail;
00314 }
00315 }
00316
00317 if(resp.status == 0) {
00318 argv[0] = "1";
00319 argv[1] = buffer;
00320 argv[3] = "Success";
00321 } else {
00322 argv[0] = "0";
00323 argv[1] = "";
00324 if(resp.n_chars) {
00325 argv[3] = buffer;
00326 } else {
00327 argv[3] = "minor braindamage, no error and no result reported.";
00328 }
00329 }
00330
00331 fail:
00332 argv[2] = aqt->preserve;
00333 did_it(GOD, aqt->thing, 0, NULL, 0, NULL, aqt->attr, argv, 4);
00334
00335 hardfail:
00336 if(running == aqt) {
00337 running = aqt->next;
00338 } else {
00339 iter = running;
00340 while (iter) {
00341 if(iter->next == aqt) {
00342 iter->next = aqt->next;
00343 break;
00344 }
00345 iter = iter->next;
00346 }
00347 }
00348
00349 close(aqt->fd);
00350 pchild = waitpid(aqt->pid, &status, WNOHANG);
00351 if(!pchild) {
00352
00353 }
00354 recent++;
00355 if(recent_tail == NULL) {
00356 aqt->next = NULL;
00357 recent_head = recent_tail = aqt;
00358 } else {
00359 recent_tail->next = aqt;
00360 recent_tail = aqt;
00361 }
00362
00363 if(recent > 20) {
00364 aqt = recent_head;
00365 recent_head = aqt->next;
00366 if(aqt->preserve) {
00367 free(aqt->preserve);
00368 aqt->preserve = NULL;
00369 }
00370 if(aqt->query) {
00371 free(aqt->query);
00372 aqt->query = NULL;
00373 }
00374 if(aqt->rdelim) {
00375 free(aqt->rdelim);
00376 aqt->rdelim = NULL;
00377 }
00378 if(aqt->cdelim) {
00379 free(aqt->cdelim);
00380 aqt->cdelim = NULL;
00381 }
00382 free(aqt);
00383 aqt = NULL;
00384 recent--;
00385 }
00386 running_queries--;
00387 sqlchild_check_queue();
00388 return;
00389 }
00390
00391 static void sqlchild_check_queue()
00392 {
00393 int fds[2];
00394 struct query_state_t *aqt;
00395 if(running_queries >= mudconf.sqlDB_max_queries)
00396 return;
00397 if(pending == NULL)
00398 return;
00399 if(dbi_state != DBIS_READY)
00400 return;
00401
00402 aqt = pending;
00403 pending = aqt->next;
00404 if(pending == NULL)
00405 pending_tail = NULL;
00406
00407 if(pipe(fds) < 0) {
00408 log_perror("SQL", "FAIL", NULL, "pipe");
00409 return;
00410 }
00411 if((aqt->pid = fork()) == 0) {
00412 aqt->fd = fds[1];
00413 close(fds[0]);
00414 unbind_signals();
00415 sqlchild_child_execute_query(aqt);
00416 exit(0);
00417 } else {
00418 running_queries++;
00419 aqt->fd = fds[0];
00420 close(fds[1]);
00421 }
00422
00423
00424
00425
00426 if(running)
00427 aqt->next = running;
00428 running = aqt;
00429
00430 event_set(&aqt->ev, aqt->fd, EV_READ, sqlchild_finish_query, aqt);
00431 event_add(&aqt->ev, &query_timeout);
00432 return;
00433 }
00434
00435
00436
00437 static void sqlchild_child_abort_query(struct query_state_t *aqt, char *error)
00438 {
00439 struct query_response resp = { DBIS_EFAIL, 0 };
00440 if(error) {
00441 resp.n_chars = strlen(error) + 1;
00442 write(aqt->fd, &resp, sizeof(resp));
00443 write(aqt->fd, error, resp.n_chars);
00444 } else {
00445 write(aqt->fd, &resp, sizeof(resp));
00446 }
00447 close(aqt->fd);
00448 exit(0);
00449 return;
00450 }
00451
00452 static void sqlchild_child_abort_query_dbi(struct query_state_t *aqt,
00453 char *error)
00454 {
00455 char *error_ptr;
00456 if(dbi_conn_error(conn, (const char **) &error_ptr) != -1)
00457 sqlchild_child_abort_query(aqt, error_ptr);
00458 else
00459 sqlchild_child_abort_query(aqt, error);
00460 return;
00461 }
00462
00463 static void sqlchild_make_connection(char db_slot)
00464 {
00465 char *db_type, *db_hostname, *db_username, *db_password, *db_database;
00466 switch (db_slot) {
00467 case 'A':
00468 db_type = mudconf.sqlDB_type_A;
00469 db_hostname = mudconf.sqlDB_hostname_A;
00470 db_username = mudconf.sqlDB_username_A;
00471 db_password = mudconf.sqlDB_password_A;
00472 db_database = mudconf.sqlDB_dbname_A;
00473 break;
00474 case 'B':
00475 db_type = mudconf.sqlDB_type_B;
00476 db_hostname = mudconf.sqlDB_hostname_B;
00477 db_username = mudconf.sqlDB_username_B;
00478 db_password = mudconf.sqlDB_password_B;
00479 db_database = mudconf.sqlDB_dbname_B;
00480 break;
00481 case 'C':
00482 db_type = mudconf.sqlDB_type_C;
00483 db_hostname = mudconf.sqlDB_hostname_C;
00484 db_username = mudconf.sqlDB_username_C;
00485 db_password = mudconf.sqlDB_password_C;
00486 db_database = mudconf.sqlDB_dbname_C;
00487 break;
00488 case 'D':
00489 db_type = mudconf.sqlDB_type_D;
00490 db_hostname = mudconf.sqlDB_hostname_D;
00491 db_username = mudconf.sqlDB_username_D;
00492 db_password = mudconf.sqlDB_password_D;
00493 db_database = mudconf.sqlDB_dbname_D;
00494 break;
00495 case 'E':
00496 db_type = mudconf.sqlDB_type_E;
00497 db_hostname = mudconf.sqlDB_hostname_E;
00498 db_username = mudconf.sqlDB_username_E;
00499 db_password = mudconf.sqlDB_password_E;
00500 db_database = mudconf.sqlDB_dbname_E;
00501 break;
00502 default:
00503 return;
00504 }
00505 conn = dbi_conn_new(db_type);
00506 if(!conn) {
00507 dprintk("dbi_conn_new() failed with db_type %s.", db_type);
00508 dbi_state = DBIS_EFAIL;
00509 return;
00510 }
00511 if(strncmp(db_type, "mysql", 128) == 0) {
00512
00513 if(strnlen(mudconf.sqlDB_mysql_socket, 128) > 0
00514 && dbi_conn_set_option(conn, "mysql_unix_socket",
00515 mudconf.sqlDB_mysql_socket)) {
00516 dprintk("failed to set mysql_unix_socket");
00517 dbi_state = DBIS_EFAIL;
00518 return;
00519 }
00520 }
00521 if(strncmp(db_type, "sqlite", 128) == 0
00522 && strnlen(mudconf.sqlDB_sqlite_dbdir, 128) > 0
00523 && dbi_conn_set_option(conn, "sqlite_dbdir",
00524 mudconf.sqlDB_sqlite_dbdir)) {
00525 dprintk("failed to set sqlite dir.");
00526 dbi_state = DBIS_EFAIL;
00527 return;
00528 }
00529 if(db_hostname && dbi_conn_set_option(conn, "host", db_hostname)) {
00530 dprintk("failed to set hostname");
00531 dbi_state = DBIS_EFAIL;
00532 return;
00533 }
00534 if(db_username && dbi_conn_set_option(conn, "username", db_username)) {
00535 dprintk("failed to set username");
00536 dbi_state = DBIS_EFAIL;
00537 return;
00538 }
00539 if(db_password && dbi_conn_set_option(conn, "password", db_password)) {
00540 dprintk("failed to set password");
00541 dbi_state = DBIS_EFAIL;
00542 return;
00543 }
00544 if(db_database && dbi_conn_set_option(conn, "dbname", db_database)) {
00545 dprintk("failed to set database");
00546 dbi_state = DBIS_EFAIL;
00547 return;
00548 }
00549 dprintk("started.");
00550 return;
00551 }
00552
00553 static char *sqlchild_sanitize_string(char *input, int length)
00554 {
00555 char *retval = malloc(length + 1);
00556 int i = 0;
00557 memset(retval, 0, length + 1);
00558 for(i = 0; input[i] && i < length; i++) {
00559 if(isprint(input[i])) {
00560 retval[i] = input[i];
00561 } else {
00562 retval[i] = ' ';
00563 }
00564 }
00565 dprintk("length: %d, i is %d, terimnal character is 0x%02x.", length, i, input[i]);
00566 return retval;
00567 }
00568
00569 static void sqlchild_child_execute_query(struct query_state_t *aqt)
00570 {
00571 struct query_response resp = { DBIS_READY, -1 };
00572 dbi_result result;
00573 int rows, fields, i, ii, retval;
00574 char output_buffer[LBUF_SIZE], *ptr, *eptr, *delim;
00575 int binary_length;
00576 char time_buffer[64];
00577 int length = 0;
00578 struct tm tm;
00579
00580 long long type_int;
00581 double type_fp;
00582 char *type_string, *binbuffer;
00583 time_t type_time;
00584
00585 ptr = output_buffer;
00586 eptr = ptr + (LBUF_SIZE - 1);
00587 *ptr = '\0';
00588
00589
00590
00591 sqlchild_make_connection(aqt->slot);
00592 if(!conn) {
00593 sqlchild_child_abort_query(aqt, "failed to create connection");
00594 return;
00595 }
00596 if(dbi_state != DBIS_READY) {
00597 sqlchild_child_abort_query_dbi(aqt,
00598 "unknown error in sqlchild_make_connection");
00599 return;
00600 }
00601
00602 if(!conn) {
00603 sqlchild_child_abort_query_dbi(aqt,
00604 "unknown error in sqlchild_make_connection");
00605 return;
00606 }
00607
00608 if(dbi_conn_connect(conn) != 0) {
00609 sqlchild_child_abort_query_dbi(aqt, "dbi_conn_connect failed");
00610 return;
00611 }
00612
00613 result = dbi_conn_query(conn, aqt->query);
00614 if(result == NULL) {
00615 sqlchild_child_abort_query_dbi(aqt,
00616 "unknown error in dbi_conn_query");
00617 return;
00618 }
00619
00620 rows = dbi_result_get_numrows(result);
00621 fields = dbi_result_get_numfields(result);
00622
00623 delim = NULL;
00624
00625 while (dbi_result_next_row(result)) {
00626 if((eptr <= ptr) || ((eptr-ptr) < 10)) {
00627
00628 sqlchild_child_abort_query(aqt, "result too long");
00629 return;
00630 }
00631
00632 if(delim != NULL) {
00633 ptr += snprintf(ptr, eptr - ptr, aqt->rdelim);
00634 }
00635
00636 for(i = 1; i <= fields; i++) {
00637 if((eptr <= ptr) || ((eptr-ptr) < 10)) {
00638
00639 sqlchild_child_abort_query(aqt, "result too long");
00640 return;
00641 }
00642
00643 if(fields == i)
00644 delim = "";
00645 else
00646 delim = aqt->cdelim;
00647
00648
00649 switch (dbi_result_get_field_type_idx(result, i)) {
00650 case DBI_TYPE_INTEGER:
00651 type_int = dbi_result_get_longlong_idx(result, i);
00652 ptr += snprintf(ptr, eptr - ptr, "%lld%s", type_int, delim);
00653 break;
00654
00655 case DBI_TYPE_DECIMAL:
00656 type_fp = dbi_result_get_double_idx(result, i);
00657 ptr += snprintf(ptr, eptr - ptr, "%f%s", type_fp, delim);
00658 break;
00659
00660 case DBI_TYPE_STRING:
00661 type_string = dbi_result_get_string_copy_idx(result, i);
00662 ptr += snprintf(ptr, eptr - ptr, "%s%s", type_string, delim);
00663 free(type_string);
00664 break;
00665
00666 case DBI_TYPE_BINARY:
00667 binary_length = dbi_result_get_field_length_idx(result, i);
00668 if(binary_length) {
00669 type_string = (char *)dbi_result_get_binary_idx(result, i);
00670 if(strncmp(type_string, "ERROR", 5)==0) {
00671 sqlchild_child_abort_query(aqt, "fucked.");
00672 return;
00673 }
00674 binbuffer = sqlchild_sanitize_string(type_string, binary_length);
00675 ptr += snprintf(ptr, eptr - ptr, "%s%s", binbuffer, delim);
00676 free(binbuffer);
00677 } else {
00678 ptr += snprintf(ptr, eptr - ptr, "%s", delim);
00679 }
00680
00681 break;
00682
00683 case DBI_TYPE_DATETIME:
00684 type_time = dbi_result_get_datetime_idx(result, i);
00685 localtime_r(&type_time, &tm);
00686 asctime_r(&tm, time_buffer);
00687 ptr += snprintf(ptr, eptr - ptr, "%s%s", time_buffer, delim);
00688 break;
00689
00690 default:
00691 sqlchild_child_abort_query(aqt, "unknown type");
00692 return;
00693 }
00694
00695 if((eptr - ptr) < 10) {
00696 sqlchild_child_abort_query(aqt, "result too large");
00697 return;
00698 }
00699
00700 }
00701 }
00702
00703 *ptr++ = '\0';
00704 resp.n_chars = eptr - output_buffer;
00705 eptr = ptr;
00706
00707
00708 write(aqt->fd, &resp, sizeof(struct query_response));
00709 ptr = output_buffer;
00710 while (ptr < eptr) {
00711 retval = write(aqt->fd, ptr, eptr - ptr);
00712 ptr += retval;
00713 }
00714 close(aqt->fd);
00715 return;
00716 }
00717
00718 #endif