src/sqlchild.c

Go to the documentation of this file.
00001 /*
00002  * sqlchild.c
00003  *
00004  * Copyright (c) 2004,2005 Martin Murray <mmurray@monkey.org>
00005  * All rights reserved.
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 /* /TODO
00028  * String sanitization.
00029  *  - dbi_driver_quote_string_copy ?
00030  * 
00031  * Handle Timezones in DATETIME
00032  *
00033  * Handle final write() failing.
00034  * 
00035  * Correct handling of TEXT data type.
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 /* do_query entrypoint */
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 /* HOST FUNCTIONS */
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         /*dprintk("receiving response for query %d", aqt->serial); */
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         /*      dprintk("%d exited abnormally.", aqt->pid); */
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 /*      dprintk("waiting on sqlchild pid %d executing request %d", aqt->pid,
00424                         aqt->serial);
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 /* CHILD FUNCTIONS */
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                 // dbi_conn_set_option(conn, "mysql_include_trailing_null", 1);
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         /* dprintk("executing query %d.", aqt->serial); */
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                         // XXX: quick hack. I should check my snprintf() return values.
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                                 // XXX: quick hack. I should check my snprintf() return values.
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                         // XXX: handle error values form snprintf()
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         // XXX: handle failure
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 /* SQL_SUPPORT */

Generated on Mon May 28 04:25:25 2007 for BattletechMUX by  doxygen 1.4.7