diff options
-rw-r--r-- | src/sledjchisl/sledjchisl.c | 757 |
1 files changed, 402 insertions, 355 deletions
diff --git a/src/sledjchisl/sledjchisl.c b/src/sledjchisl/sledjchisl.c index 537b435..a8d8b3f 100644 --- a/src/sledjchisl/sledjchisl.c +++ b/src/sledjchisl/sledjchisl.c | |||
@@ -375,7 +375,6 @@ struct _reqData | |||
375 | qhashtbl_t *configs, *queries, *body, *cookies, *headers, *valid, *stuff, *database, *Rcookies, *Rheaders; | 375 | qhashtbl_t *configs, *queries, *body, *cookies, *headers, *valid, *stuff, *database, *Rcookies, *Rheaders; |
376 | char *Scheme, *Host, *Method, *Script, *Path, *RUri, *doit, *form, *output, *outQuery; | 376 | char *Scheme, *Host, *Method, *Script, *Path, *RUri, *doit, *form, *output, *outQuery; |
377 | sesh shs, *lnk; | 377 | sesh shs, *lnk; |
378 | MYSQL *db; | ||
379 | gridStats *stats; | 378 | gridStats *stats; |
380 | qlist_t *errors, *messages; | 379 | qlist_t *errors, *messages; |
381 | qgrow_t *reply; | 380 | qgrow_t *reply; |
@@ -411,6 +410,9 @@ char toybuf[4096]; | |||
411 | lua_State *L; | 410 | lua_State *L; |
412 | qhashtbl_t *configs; | 411 | qhashtbl_t *configs; |
413 | MYSQL *database, *dbconn; | 412 | MYSQL *database, *dbconn; |
413 | unsigned int dbTimeout; | ||
414 | struct timespec dbLast; | ||
415 | my_bool dbReconnect; | ||
414 | gridStats *stats; | 416 | gridStats *stats; |
415 | boolean isTmux = 0; | 417 | boolean isTmux = 0; |
416 | boolean isWeb = 0; | 418 | boolean isWeb = 0; |
@@ -882,36 +884,203 @@ static void dumpArray(int d, char **ar) | |||
882 | } | 884 | } |
883 | 885 | ||
884 | 886 | ||
885 | /* How to deal with prepared SQL statements. | ||
886 | http://karlssonondatabases.blogspot.com/2010/07/prepared-statements-are-they-useful-or.html | ||
887 | https://blog.cotten.io/a-taste-of-mysql-in-c-87c5de84a31d?gi=ab3dd1425b29 | ||
888 | https://raspberry-projects.com/pi/programming-in-c/databases-programming-in-c/mysql/accessing-the-database | ||
889 | 887 | ||
890 | IG and CG now both have sims connected to other grids, so some sort of | 888 | typedef struct _dbFields dbFields; |
891 | multi database solution would be good, then we can run the grid and the | 889 | struct _dbFields |
892 | external sims all in one. | 890 | { |
891 | qlisttbl_t *flds; | ||
892 | int count; | ||
893 | }; | ||
894 | typedef struct _dbField dbField; | ||
895 | struct _dbField | ||
896 | { | ||
897 | char *name; | ||
898 | enum enum_field_types type; | ||
899 | unsigned long length; | ||
900 | unsigned int flags; | ||
901 | unsigned int decimals; | ||
902 | }; | ||
893 | 903 | ||
894 | Not sure if this'll work with Count(*). | 904 | void dbFreeFields(dbFields *flds, boolean all) |
905 | { | ||
906 | flds->count--; | ||
895 | 907 | ||
896 | --------------------------------------------- | 908 | // TODO - sigh, looks to be inconsistant why some do and some don't leak. |
909 | // I guess the ones that don't leak are the ones that crash? | ||
910 | // It's only a tiny leak anyway, 80 bytes total. | ||
911 | // if ((0 >= flds->count) || all) // CRASHY | ||
912 | if ((0 >= flds->count)) // LEAKY | ||
913 | { | ||
914 | qlisttbl_obj_t obj; | ||
897 | 915 | ||
898 | The complicated bit is the binds. | 916 | memset((void *) &obj, 0, sizeof(obj)); |
917 | flds->flds->lock(flds->flds); | ||
918 | while(flds->flds->getnext(flds->flds, &obj, NULL, false) == true) | ||
919 | { | ||
920 | dbField *fld = (dbField *) obj.data; | ||
921 | free(fld->name); | ||
922 | } | ||
923 | flds->flds->unlock(flds->flds); | ||
924 | flds->flds->free(flds->flds); | ||
925 | flds->flds = NULL; | ||
926 | free(flds); | ||
927 | } | ||
928 | } | ||
899 | 929 | ||
900 | You are binding field values to C memory locations. | 930 | enum dbCommandType |
901 | The parameters and returned fields need binds. | 931 | { |
902 | Mostly seems to be the value parts of the SQL statements. | 932 | CT_SELECT, |
933 | CT_CREATE, | ||
934 | CT_UPDATE, | ||
935 | CT_NONE | ||
936 | }; | ||
903 | 937 | ||
904 | I suspect most will be of the form - | 938 | typedef struct _dbRequest dbRequest; |
905 | ... WHERE x=? and foo=? | 939 | struct _dbRequest |
906 | INSERT INTO table VALUES (?,?,?) | 940 | { |
907 | UPDATE table SET x=?, foo=? WHERE id=? | 941 | char *table, *join, *where, *order, *sql; |
942 | MYSQL_STMT *prep; // NOTE - executing it stores state in this. | ||
943 | dbFields *fields; | ||
944 | int inCount, outCount, rowCount; | ||
945 | char **inParams, **outParams; | ||
946 | MYSQL_BIND *inBind, *outBind; | ||
947 | rowData *rows; | ||
948 | my_ulonglong count; | ||
949 | enum dbCommandType type; | ||
950 | boolean freeOutParams; | ||
951 | }; | ||
908 | 952 | ||
909 | A multi table update - | 953 | void dbFreeRequest(dbRequest *req, boolean all) |
910 | UPDATE items,month SET items.price=month.price WHERE items.id=month.id; | 954 | { |
911 | */ | 955 | int i; |
956 | |||
957 | D("Cleaning up prepared database request %s - %s %d %d", req->table, req->where, req->outCount, req->inCount); | ||
958 | |||
959 | if (NULL != req->outBind) | ||
960 | { | ||
961 | for (i = 0; i < req->outCount; i++) | ||
962 | { | ||
963 | if (NULL != req->outBind[i].buffer) free(req->outBind[i].buffer); | ||
964 | if (NULL != req->outBind[i].length) free(req->outBind[i].length); | ||
965 | if (NULL != req->outBind[i].error) free(req->outBind[i].error); | ||
966 | if (NULL != req->outBind[i].is_null) free(req->outBind[i].is_null); | ||
967 | } | ||
968 | free(req->outBind); | ||
969 | req->outBind = NULL; | ||
970 | } | ||
971 | else | ||
972 | D(" No out binds to clean up for %s - %s.", req->table, req->where); | ||
973 | if (NULL != req->inBind) | ||
974 | { | ||
975 | for (i = 0; i < req->inCount; i++) | ||
976 | { | ||
977 | if (NULL != req->inBind[i].buffer) free(req->inBind[i].buffer); | ||
978 | if (NULL != req->inBind[i].length) free(req->inBind[i].length); | ||
979 | if (NULL != req->inBind[i].error) free(req->inBind[i].error); | ||
980 | if (NULL != req->inBind[i].is_null) free(req->inBind[i].is_null); | ||
981 | } | ||
982 | free(req->inBind); | ||
983 | req->inBind = NULL; | ||
984 | } | ||
985 | else | ||
986 | D(" No in binds to clean up for %s - %s.", req->table, req->where); | ||
987 | |||
988 | if (req->freeOutParams && all) | ||
989 | { | ||
990 | if (NULL != req->outParams) | ||
991 | { | ||
992 | free(req->outParams); | ||
993 | req->outParams = NULL; | ||
994 | } | ||
995 | else | ||
996 | D(" No out params to clean up for %s - %s.", req->table, req->where); | ||
997 | } | ||
998 | if (NULL != req->sql) free(req->sql); | ||
999 | else | ||
1000 | D(" No SQL to clean up for %s - %s.", req->table, req->where); | ||
1001 | req->sql = NULL; | ||
1002 | if (NULL != req->prep) | ||
1003 | { | ||
1004 | if (0 != mysql_stmt_close(req->prep)) | ||
1005 | C(" Unable to close the prepared statement!"); | ||
1006 | req->prep = NULL; | ||
1007 | } | ||
1008 | |||
1009 | if (all) | ||
1010 | { | ||
1011 | if (NULL != req->fields) | ||
1012 | { | ||
1013 | dbFreeFields(req->fields, all); | ||
1014 | req->fields = NULL; | ||
1015 | } | ||
1016 | else | ||
1017 | D(" No fields to clean up for %s - %s.", req->table, req->where); | ||
1018 | } | ||
1019 | } | ||
1020 | |||
1021 | void freeDb(boolean all) | ||
1022 | { | ||
1023 | dbRequest **rq; | ||
1024 | |||
1025 | if (dbRequests) | ||
1026 | { | ||
1027 | if (all) | ||
1028 | { | ||
1029 | while (NULL != (rq = (dbRequest **) dbRequests->popfirst(dbRequests, NULL))) | ||
1030 | { | ||
1031 | dbFreeRequest(*rq, all); | ||
1032 | free(rq); | ||
1033 | } | ||
1034 | dbRequests->free(dbRequests); | ||
1035 | dbRequests = NULL; | ||
1036 | } | ||
1037 | else | ||
1038 | { | ||
1039 | qlist_obj_t obj; | ||
1040 | |||
1041 | memset((void*)&obj, 0, sizeof(obj)); // must be cleared before call | ||
1042 | dbRequests->lock(dbRequests); | ||
1043 | while (dbRequests->getnext(dbRequests, &obj, false) == true) | ||
1044 | dbFreeRequest(*((dbRequest **) obj.data), all); | ||
1045 | dbRequests->unlock(dbRequests); | ||
1046 | } | ||
1047 | } | ||
1048 | |||
1049 | if (database) mysql_close(database); | ||
1050 | database = NULL; | ||
1051 | mysql_library_end(); | ||
1052 | } | ||
912 | 1053 | ||
913 | static boolean dbConnect() | 1054 | static boolean dbConnect() |
914 | { | 1055 | { |
1056 | database = mysql_init(NULL); | ||
1057 | if (NULL == database) | ||
1058 | { | ||
1059 | E("mysql_init() failed - %s", mysql_error(database)); | ||
1060 | return FALSE; | ||
1061 | } | ||
1062 | |||
1063 | /* TODO - dammit, no mysql_get_option(), MariaDB docs say mysql_get_optionv(), which doesn't exist either. | ||
1064 | Says "This function was added in MariaDB Connector/C 3.0.0.", I have MariaDB / MySQL client version: 10.1.44-MariaDB. | ||
1065 | |||
1066 | if (mysql_get_option(database, MYSQL_OPT_CONNECT_TIMEOUT, &dbTimeout)) | ||
1067 | E("mysql_get_option(MYSQL_OPT_CONNECT_TIMEOUT) failed - %s", mysql_error(database)); | ||
1068 | else | ||
1069 | D("Database MYSQL_OPT_CONNECT_TIMEOUT = %d", dbTimeout); | ||
1070 | |||
1071 | if (mysql_get_option(database, MYSQL_OPT_RECONNECT, &dbReconnect)) | ||
1072 | E("mysql_get_option(MYSQL_OPT_RECONNECT) failed - %s", mysql_error(database)); | ||
1073 | else | ||
1074 | D("Database MYSQL_OPT_RECONNECT = %d", (int) dbReconnect); | ||
1075 | */ | ||
1076 | |||
1077 | // Seems best to disable auto-reconnect, so I have more control over reconnections. | ||
1078 | dbReconnect = 0; | ||
1079 | if (mysql_options(database, MYSQL_OPT_RECONNECT, &dbReconnect)) | ||
1080 | E("mysql_options(MYSQL_OPT_RECONNECT) failed - %s", mysql_error(database)); | ||
1081 | else | ||
1082 | D("Database MYSQL_OPT_RECONNECT is now %d", (int) dbReconnect); | ||
1083 | |||
915 | dbconn = mysql_real_connect(database, | 1084 | dbconn = mysql_real_connect(database, |
916 | getStrH(configs, "Data Source"), | 1085 | getStrH(configs, "Data Source"), |
917 | getStrH(configs, "User ID"), | 1086 | getStrH(configs, "User ID"), |
@@ -925,6 +1094,21 @@ static boolean dbConnect() | |||
925 | E("mysql_real_connect() failed - %s", mysql_error(database)); | 1094 | E("mysql_real_connect() failed - %s", mysql_error(database)); |
926 | return FALSE; | 1095 | return FALSE; |
927 | } | 1096 | } |
1097 | |||
1098 | // Just set the fucking thing. Pffft. | ||
1099 | dbTimeout = 60 * 60 * 24 * 7 * 52; | ||
1100 | dbTimeout = 100; | ||
1101 | char *sql = xmprintf("SET SESSION wait_timeout=%d", (int) dbTimeout); | ||
1102 | |||
1103 | if (mysql_query(database, sql)) | ||
1104 | E("SET SESSION wait_timeout=%d failed - %s", (int) dbTimeout, mysql_error(database)); | ||
1105 | else | ||
1106 | D("Database wait_timeout = %d", (int) dbReconnect); | ||
1107 | free(sql); | ||
1108 | |||
1109 | if (-1 == clock_gettime(CLOCK_REALTIME, &dbLast)) | ||
1110 | perror_msg("Unable to get the time."); | ||
1111 | |||
928 | return TRUE; | 1112 | return TRUE; |
929 | } | 1113 | } |
930 | 1114 | ||
@@ -933,43 +1117,41 @@ static boolean dbConnect() | |||
933 | // 1129? 1152? 1184? 1218? 1927 3032? 4150? | 1117 | // 1129? 1152? 1184? 1218? 1927 3032? 4150? |
934 | // "server has gone away" isn't listed there, that's the one I was getting. Pffft | 1118 | // "server has gone away" isn't listed there, that's the one I was getting. Pffft |
935 | // It's 2006, https://dev.mysql.com/doc/refman/8.0/en/gone-away.html | 1119 | // It's 2006, https://dev.mysql.com/doc/refman/8.0/en/gone-away.html |
936 | // Though none of the mentioned reasons make sense here. | ||
937 | // Ah it could be "connection inactive for 8 hours". | 1120 | // Ah it could be "connection inactive for 8 hours". |
938 | // Which might be why OpenSim opens a new connection for EVERYTHING. | 1121 | // Which might be why OpenSim opens a new connection for EVERYTHING. |
939 | // TODO - see if I can either find out what the time out is, or just check and re open for each db thing. | 1122 | // https://dev.mysql.com/doc/refman/5.7/en/c-api-auto-reconnect.html |
940 | // int mysql_ping(MYSQL * mysql); // https://mariadb.com/kb/en/mysql_ping/ | 1123 | // Has more details. |
941 | // "If it has gone down, and global option reconnect is enabled an automatic reconnection is attempted." | 1124 | static boolean dbCheckError(char *error, char *sql) |
942 | // "Returns zero on success, nonzero if an error occured." | ||
943 | // "resources bundled to the connection (prepared statements, locks, temporary tables, ...) will be released." sigh | ||
944 | // Quick'n'dirty until this is properly event driven - have a cron job curl the stats page every hour. | ||
945 | static boolean dbCheckError(MYSQL *db, char *error, char *sql) | ||
946 | { | 1125 | { |
947 | int e = mysql_errno(db); | 1126 | int e = mysql_errno(database); |
948 | 1127 | ||
949 | E("MariaDB error %d - %s: %s\n%s", e, error, mysql_error(db), sql); | 1128 | E("MariaDB error %d - %s: %s\n%s", e, error, mysql_error(database), sql); |
950 | if (2006 == e) | 1129 | if (2006 == e) |
1130 | { | ||
1131 | W("Reconnecting to database."); | ||
1132 | freeDb(false); | ||
951 | return dbConnect(); | 1133 | return dbConnect(); |
1134 | } | ||
952 | 1135 | ||
953 | return FALSE; | 1136 | return FALSE; |
954 | } | 1137 | } |
955 | 1138 | // "Statement execute failed 2013: Lost connection to MySQL server during query" | |
956 | typedef struct _dbFields dbFields; | 1139 | static boolean dbStmtCheckError(dbRequest *req, char *error, char *sql) |
957 | struct _dbFields | ||
958 | { | 1140 | { |
959 | qlisttbl_t *flds; | 1141 | int e = mysql_stmt_errno(req->prep); |
960 | int count; | 1142 | |
961 | }; | 1143 | E("MariaDB prepared statement error %d - %s: %s\n%s", e, error, mysql_stmt_error(req->prep), sql); |
962 | typedef struct _dbField dbField; | 1144 | if (2013 == e) |
963 | struct _dbField | 1145 | { |
964 | { | 1146 | W("Reconnecting to database."); |
965 | char *name; | 1147 | freeDb(false); |
966 | enum enum_field_types type; | 1148 | return dbConnect(); |
967 | unsigned long length; | 1149 | } |
968 | unsigned int flags; | 1150 | |
969 | unsigned int decimals; | 1151 | return FALSE; |
970 | }; | 1152 | } |
971 | 1153 | ||
972 | dbFields *dbGetFields(MYSQL *db, char *table) | 1154 | dbFields *dbGetFields(char *table) |
973 | { | 1155 | { |
974 | static qhashtbl_t *tables = NULL; | 1156 | static qhashtbl_t *tables = NULL; |
975 | if (NULL == tables) tables = qhashtbl(0, 0); | 1157 | if (NULL == tables) tables = qhashtbl(0, 0); |
@@ -982,33 +1164,33 @@ dbFields *dbGetFields(MYSQL *db, char *table) | |||
982 | char *sql = xmprintf("SELECT * FROM %s LIMIT 0", table); | 1164 | char *sql = xmprintf("SELECT * FROM %s LIMIT 0", table); |
983 | 1165 | ||
984 | d("Getting field metadata for %s", table); | 1166 | d("Getting field metadata for %s", table); |
985 | if (mysql_query(db, sql)) | 1167 | if (mysql_query(database, sql)) |
986 | { | 1168 | { |
987 | // E("MariaDB error %d - Query failed 0: %s\n%s", mysql_errno(db), mysql_error(db), sql); | 1169 | // E("MariaDB error %d - Query failed 0: %s\n%s", mysql_errno(database), mysql_error(database), sql); |
988 | if (dbCheckError(db, "Query failed 0", sql)) | 1170 | if (dbCheckError("Query failed 0", sql)) |
989 | { | 1171 | { |
990 | ret = dbGetFields(db, table); | 1172 | ret = dbGetFields(table); |
991 | free(sql); | 1173 | free(sql); |
992 | return ret; | 1174 | return ret; |
993 | } | 1175 | } |
994 | } | 1176 | } |
995 | else | 1177 | else |
996 | { | 1178 | { |
997 | MYSQL_RES *res = mysql_store_result(db); | 1179 | MYSQL_RES *res = mysql_store_result(database); |
998 | 1180 | ||
999 | if (!res) | 1181 | if (!res) |
1000 | E("MariaDB error %d - Couldn't get results set from %s\n %s", mysql_errno(db), mysql_error(db), sql); | 1182 | E("MariaDB error %d - Couldn't get results set from %s\n %s", mysql_errno(database), mysql_error(database), sql); |
1001 | else | 1183 | else |
1002 | { | 1184 | { |
1003 | MYSQL_FIELD *fields = mysql_fetch_fields(res); | 1185 | MYSQL_FIELD *fields = mysql_fetch_fields(res); |
1004 | 1186 | ||
1005 | if (!fields) | 1187 | if (!fields) |
1006 | E("MariaDB error %d - Failed fetching fields: %s", mysql_errno(db), mysql_error(db)); | 1188 | E("MariaDB error %d - Failed fetching fields: %s", mysql_errno(database), mysql_error(database)); |
1007 | else | 1189 | else |
1008 | { | 1190 | { |
1009 | unsigned int i, num_fields = mysql_num_fields(res); | 1191 | unsigned int i, num_fields = mysql_num_fields(res); |
1010 | 1192 | ||
1011 | ret = xmalloc(sizeof(dbFields)); | 1193 | ret = xmalloc(sizeof(dbFields)); // Little bit LEAKY |
1012 | ret->flds = qlisttbl(QLISTTBL_UNIQUE | QLISTTBL_LOOKUPFORWARD); | 1194 | ret->flds = qlisttbl(QLISTTBL_UNIQUE | QLISTTBL_LOOKUPFORWARD); |
1013 | ret->count = 1; | 1195 | ret->count = 1; |
1014 | for (i = 0; i < num_fields; i++) | 1196 | for (i = 0; i < num_fields; i++) |
@@ -1035,62 +1217,61 @@ d("Getting field metadata for %s", table); | |||
1035 | return ret; | 1217 | return ret; |
1036 | } | 1218 | } |
1037 | 1219 | ||
1038 | void dbFreeFields(dbFields *flds) | ||
1039 | { | ||
1040 | flds->count--; | ||
1041 | if (0 >= flds->count) | ||
1042 | { | ||
1043 | qlisttbl_obj_t obj; | ||
1044 | 1220 | ||
1045 | memset((void *) &obj, 0, sizeof(obj)); | 1221 | /* How to deal with prepared SQL statements. |
1046 | flds->flds->lock(flds->flds); | 1222 | http://karlssonondatabases.blogspot.com/2010/07/prepared-statements-are-they-useful-or.html |
1047 | while(flds->flds->getnext(flds->flds, &obj, NULL, false) == true) | 1223 | https://blog.cotten.io/a-taste-of-mysql-in-c-87c5de84a31d?gi=ab3dd1425b29 |
1048 | { | 1224 | https://raspberry-projects.com/pi/programming-in-c/databases-programming-in-c/mysql/accessing-the-database |
1049 | dbField *fld = (dbField *) obj.data; | ||
1050 | free(fld->name); | ||
1051 | } | ||
1052 | flds->flds->unlock(flds->flds); | ||
1053 | flds->flds->free(flds->flds); | ||
1054 | free(flds); | ||
1055 | } | ||
1056 | } | ||
1057 | 1225 | ||
1058 | enum dbCommandType | 1226 | IG and CG now both have sims connected to other grids, so some sort of |
1059 | { | 1227 | multi database solution would be good, then we can run the grid and the |
1060 | CT_SELECT, | 1228 | external sims all in one. |
1061 | CT_CREATE, | ||
1062 | CT_UPDATE, | ||
1063 | CT_NONE | ||
1064 | }; | ||
1065 | 1229 | ||
1066 | typedef struct _dbRequest dbRequest; | 1230 | Not sure if this'll work with Count(*). |
1067 | struct _dbRequest | 1231 | |
1068 | { | 1232 | --------------------------------------------- |
1069 | MYSQL *db; | 1233 | |
1070 | char *table, *join, *where, *order, *sql; | 1234 | The complicated bit is the binds. |
1071 | MYSQL_STMT *prep; // NOTE - executing it stores state in this. | 1235 | |
1072 | dbFields *fields; | 1236 | You are binding field values to C memory locations. |
1073 | qlisttbl_t *flds; | 1237 | The parameters and returned fields need binds. |
1074 | int inCount, outCount, rowCount; | 1238 | Mostly seems to be the value parts of the SQL statements. |
1075 | char **inParams, **outParams; | 1239 | |
1076 | MYSQL_BIND *inBind, *outBind; | 1240 | I suspect most will be of the form - |
1077 | rowData *rows; | 1241 | ... WHERE x=? and foo=? |
1078 | my_ulonglong count; | 1242 | INSERT INTO table VALUES (?,?,?) |
1079 | enum dbCommandType type; | 1243 | UPDATE table SET x=?, foo=? WHERE id=? |
1080 | boolean freeOutParams; | 1244 | |
1081 | }; | 1245 | A multi table update - |
1246 | UPDATE items,month SET items.price=month.price WHERE items.id=month.id; | ||
1247 | */ | ||
1082 | 1248 | ||
1083 | int dbDoSomething(dbRequest *req, boolean count, ...) | 1249 | int dbDoSomething(dbRequest *req, boolean count, ...) |
1084 | { | 1250 | { |
1085 | int ret = 0; | 1251 | int ret = 0; |
1086 | va_list ap; | 1252 | va_list ap; |
1087 | struct timespec now, then; | 1253 | struct timespec then; |
1088 | int i, j; | 1254 | int i, j; |
1089 | MYSQL_RES *prepare_meta_result = NULL; | 1255 | MYSQL_RES *prepare_meta_result = NULL; |
1090 | 1256 | ||
1091 | if (-1 == clock_gettime(CLOCK_REALTIME, &then)) | 1257 | if (-1 == clock_gettime(CLOCK_REALTIME, &then)) |
1092 | perror_msg("Unable to get the time."); | 1258 | perror_msg("Unable to get the time."); |
1093 | 1259 | ||
1260 | double n = (dbLast.tv_sec * 1000000000.0) + dbLast.tv_nsec; | ||
1261 | double t = (then.tv_sec * 1000000000.0) + then.tv_nsec; | ||
1262 | |||
1263 | t("Database timeout test %lf > %lf", ((t - n) / 1000000000.0), (dbTimeout / 2.0)); | ||
1264 | if (((t - n) / 1000000000.0) > (dbTimeout / 2.0)) | ||
1265 | { | ||
1266 | T("Avoid database timeout of %d seconds, pinging it.", dbTimeout); | ||
1267 | if (0 != mysql_ping(database)) | ||
1268 | { | ||
1269 | W("Reconnecting to database."); | ||
1270 | freeDb(false); | ||
1271 | dbConnect(); | ||
1272 | } | ||
1273 | } | ||
1274 | |||
1094 | va_start(ap, count); | 1275 | va_start(ap, count); |
1095 | 1276 | ||
1096 | if (NULL == req->prep) | 1277 | if (NULL == req->prep) |
@@ -1100,14 +1281,13 @@ int dbDoSomething(dbRequest *req, boolean count, ...) | |||
1100 | if (0 == req->type) | 1281 | if (0 == req->type) |
1101 | req->type = CT_SELECT; | 1282 | req->type = CT_SELECT; |
1102 | 1283 | ||
1103 | req->fields = dbGetFields(req->db, req->table); | 1284 | req->fields = dbGetFields(req->table); |
1104 | if (NULL == req->fields) | 1285 | if (NULL == req->fields) |
1105 | { | 1286 | { |
1106 | E("Unknown fields for table %s.", req->table); | 1287 | E("Unknown fields for table %s.", req->table); |
1107 | ret++; | 1288 | ret++; |
1108 | goto end; | 1289 | goto end; |
1109 | } | 1290 | } |
1110 | req->flds = req->fields->flds; | ||
1111 | 1291 | ||
1112 | switch (req->type) | 1292 | switch (req->type) |
1113 | { | 1293 | { |
@@ -1189,7 +1369,7 @@ int dbDoSomething(dbRequest *req, boolean count, ...) | |||
1189 | 1369 | ||
1190 | d("New SQL statement - %s", req->sql); | 1370 | d("New SQL statement - %s", req->sql); |
1191 | // prepare statement with the other fields | 1371 | // prepare statement with the other fields |
1192 | req->prep = mysql_stmt_init(req->db); | 1372 | req->prep = mysql_stmt_init(database); |
1193 | if (NULL == req->prep) | 1373 | if (NULL == req->prep) |
1194 | { | 1374 | { |
1195 | E("Statement prepare init failed: %s\n", mysql_stmt_error(req->prep)); | 1375 | E("Statement prepare init failed: %s\n", mysql_stmt_error(req->prep)); |
@@ -1218,7 +1398,7 @@ d("New SQL statement - %s", req->sql); | |||
1218 | //W("Allocated %d %d inBinds for %s", i, req->inCount, req->sql); | 1398 | //W("Allocated %d %d inBinds for %s", i, req->inCount, req->sql); |
1219 | for (i = 0; i < req->inCount; i++) | 1399 | for (i = 0; i < req->inCount; i++) |
1220 | { | 1400 | { |
1221 | dbField *fld = req->flds->get(req->flds, req->inParams[i], NULL, false); | 1401 | dbField *fld = req->fields->flds->get(req->fields->flds, req->inParams[i], NULL, false); |
1222 | 1402 | ||
1223 | if (NULL == fld) | 1403 | if (NULL == fld) |
1224 | { | 1404 | { |
@@ -1359,15 +1539,15 @@ I("count!!!!!!!!!!!!!!!!"); | |||
1359 | req->freeOutParams = TRUE; | 1539 | req->freeOutParams = TRUE; |
1360 | qlisttbl_obj_t obj; | 1540 | qlisttbl_obj_t obj; |
1361 | memset((void*)&obj, 0, sizeof(obj)); | 1541 | memset((void*)&obj, 0, sizeof(obj)); |
1362 | req->flds->lock(req->flds); | 1542 | req->fields->flds->lock(req->fields->flds); |
1363 | while (req->flds->getnext(req->flds, &obj, NULL, false) == true) | 1543 | while (req->fields->flds->getnext(req->fields->flds, &obj, NULL, false) == true) |
1364 | { | 1544 | { |
1365 | dbField *fld = (dbField *) obj.data; | 1545 | dbField *fld = (dbField *) obj.data; |
1366 | req->outParams[i] = fld->name; | 1546 | req->outParams[i] = fld->name; |
1367 | i++; | 1547 | i++; |
1368 | } | 1548 | } |
1369 | req->outParams[i] = NULL; | 1549 | req->outParams[i] = NULL; |
1370 | req->flds->unlock(req->flds); | 1550 | req->fields->flds->unlock(req->fields->flds); |
1371 | } | 1551 | } |
1372 | if (i != req->outCount) | 1552 | if (i != req->outCount) |
1373 | { | 1553 | { |
@@ -1379,7 +1559,7 @@ I("count!!!!!!!!!!!!!!!!"); | |||
1379 | //W("Allocated %d %d outBinds for %s", i, req->outCount, req->sql); | 1559 | //W("Allocated %d %d outBinds for %s", i, req->outCount, req->sql); |
1380 | for (i = 0; i < req->outCount; i++) | 1560 | for (i = 0; i < req->outCount; i++) |
1381 | { | 1561 | { |
1382 | dbField *fld = req->flds->get(req->flds, req->outParams[i], NULL, false); | 1562 | dbField *fld = req->fields->flds->get(req->fields->flds, req->outParams[i], NULL, false); |
1383 | 1563 | ||
1384 | if (NULL == fld) | 1564 | if (NULL == fld) |
1385 | { | 1565 | { |
@@ -1503,7 +1683,7 @@ I("count!!!!!!!!!!!!!!!!"); | |||
1503 | //d("input bind for %s", req->sql); | 1683 | //d("input bind for %s", req->sql); |
1504 | for (i = 0; i < req->inCount; i++) | 1684 | for (i = 0; i < req->inCount; i++) |
1505 | { | 1685 | { |
1506 | dbField *fld = req->flds->get(req->flds, req->inParams[i], NULL, false); | 1686 | dbField *fld = req->fields->flds->get(req->fields->flds, req->inParams[i], NULL, false); |
1507 | 1687 | ||
1508 | if (NULL == fld) | 1688 | if (NULL == fld) |
1509 | { | 1689 | { |
@@ -1651,9 +1831,11 @@ I("count!!!!!!!!!!!!!!!!"); | |||
1651 | // do the prepared statement req->prep. | 1831 | // do the prepared statement req->prep. |
1652 | if (mysql_stmt_execute(req->prep)) | 1832 | if (mysql_stmt_execute(req->prep)) |
1653 | { | 1833 | { |
1654 | E("Statement execute failed %d: %s\n", mysql_stmt_errno(req->prep), mysql_stmt_error(req->prep)); | 1834 | if (dbStmtCheckError(req, "Statement failed 0", req->sql)) |
1655 | ret++; | 1835 | { |
1656 | goto freeIt; | 1836 | ret++; |
1837 | goto freeIt; | ||
1838 | } | ||
1657 | } | 1839 | } |
1658 | 1840 | ||
1659 | int fs = mysql_stmt_field_count(req->prep); | 1841 | int fs = mysql_stmt_field_count(req->prep); |
@@ -1681,7 +1863,7 @@ I("count!!!!!!!!!!!!!!!!"); | |||
1681 | 1863 | ||
1682 | for (i = 0; i < req->outCount; i++) | 1864 | for (i = 0; i < req->outCount; i++) |
1683 | { | 1865 | { |
1684 | dbField *fld = req->flds->get(req->flds, req->outParams[i], NULL, false); | 1866 | dbField *fld = req->fields->flds->get(req->fields->flds, req->outParams[i], NULL, false); |
1685 | 1867 | ||
1686 | req->rows->fieldNames[i] = fld->name; | 1868 | req->rows->fieldNames[i] = fld->name; |
1687 | if (!*(req->outBind[i].is_null)) | 1869 | if (!*(req->outBind[i].is_null)) |
@@ -1803,10 +1985,9 @@ freeIt: | |||
1803 | end: | 1985 | end: |
1804 | va_end(ap); | 1986 | va_end(ap); |
1805 | 1987 | ||
1806 | if (-1 == clock_gettime(CLOCK_REALTIME, &now)) | 1988 | if (-1 == clock_gettime(CLOCK_REALTIME, &dbLast)) |
1807 | perror_msg("Unable to get the time."); | 1989 | perror_msg("Unable to get the time."); |
1808 | double n = (now.tv_sec * 1000000000.0) + now.tv_nsec; | 1990 | n = (dbLast.tv_sec * 1000000000.0) + dbLast.tv_nsec; |
1809 | double t = (then.tv_sec * 1000000000.0) + then.tv_nsec; | ||
1810 | T("dbDoSomething(%s) took %lf seconds", req->sql, (n - t) / 1000000000.0); | 1991 | T("dbDoSomething(%s) took %lf seconds", req->sql, (n - t) / 1000000000.0); |
1811 | 1992 | ||
1812 | return ret; | 1993 | return ret; |
@@ -1838,62 +2019,7 @@ d("dbPull(Rd->database) %s = %s", where, (char *) obj.data); | |||
1838 | free(rows); | 2019 | free(rows); |
1839 | } | 2020 | } |
1840 | 2021 | ||
1841 | void dbFreeRequest(dbRequest *req) | 2022 | my_ulonglong dbCount(char *table, char *where) |
1842 | { | ||
1843 | int i; | ||
1844 | |||
1845 | D("Cleaning up prepared database request %s - %s %d %d", req->table, req->where, req->outCount, req->inCount); | ||
1846 | |||
1847 | if (NULL != req->fields) | ||
1848 | { | ||
1849 | dbFreeFields(req->fields); | ||
1850 | req->fields = NULL; | ||
1851 | } | ||
1852 | else | ||
1853 | D(" No fields to clean up for %s - %s.", req->table, req->where); | ||
1854 | |||
1855 | if (NULL != req->outBind) | ||
1856 | { | ||
1857 | for (i = 0; i < req->outCount; i++) | ||
1858 | { | ||
1859 | if (NULL != req->outBind[i].buffer) free(req->outBind[i].buffer); | ||
1860 | if (NULL != req->outBind[i].length) free(req->outBind[i].length); | ||
1861 | if (NULL != req->outBind[i].error) free(req->outBind[i].error); | ||
1862 | if (NULL != req->outBind[i].is_null) free(req->outBind[i].is_null); | ||
1863 | } | ||
1864 | free(req->outBind); | ||
1865 | } | ||
1866 | else | ||
1867 | D(" No out binds to clean up for %s - %s.", req->table, req->where); | ||
1868 | if (NULL != req->inBind) | ||
1869 | { | ||
1870 | for (i = 0; i < req->inCount; i++) | ||
1871 | { | ||
1872 | if (NULL != req->inBind[i].buffer) free(req->inBind[i].buffer); | ||
1873 | if (NULL != req->inBind[i].length) free(req->inBind[i].length); | ||
1874 | if (NULL != req->inBind[i].error) free(req->inBind[i].error); | ||
1875 | if (NULL != req->inBind[i].is_null) free(req->inBind[i].is_null); | ||
1876 | } | ||
1877 | free(req->inBind); | ||
1878 | } | ||
1879 | else | ||
1880 | D(" No in binds to clean up for %s - %s.", req->table, req->where); | ||
1881 | |||
1882 | if (req->freeOutParams) free(req->outParams); | ||
1883 | else | ||
1884 | D(" No out params to clean up for %s - %s.", req->table, req->where); | ||
1885 | if (NULL != req->sql) free(req->sql); | ||
1886 | else | ||
1887 | D(" No SQL to clean up for %s - %s.", req->table, req->where); | ||
1888 | if (NULL != req->prep) | ||
1889 | { | ||
1890 | if (0 != mysql_stmt_close(req->prep)) | ||
1891 | C(" Unable to close the prepared statement!"); | ||
1892 | req->prep = NULL; | ||
1893 | } | ||
1894 | } | ||
1895 | |||
1896 | my_ulonglong dbCount(MYSQL *db, char *table, char *where) | ||
1897 | { | 2023 | { |
1898 | my_ulonglong ret = 0; | 2024 | my_ulonglong ret = 0; |
1899 | char *sql; | 2025 | char *sql; |
@@ -1907,27 +2033,27 @@ my_ulonglong dbCount(MYSQL *db, char *table, char *where) | |||
1907 | else | 2033 | else |
1908 | sql = xmprintf("SELECT Count(*) FROM %s", table); | 2034 | sql = xmprintf("SELECT Count(*) FROM %s", table); |
1909 | 2035 | ||
1910 | if (mysql_query(db, sql)) | 2036 | if (mysql_query(database, sql)) |
1911 | { | 2037 | { |
1912 | // E("MariaDB error %d - Query failed 1: %s", mysql_errno(db), mysql_error(db)); | 2038 | // E("MariaDB error %d - Query failed 1: %s", mysql_errno(database), mysql_error(database)); |
1913 | if (dbCheckError(db, "Query failed 1", sql)) | 2039 | if (dbCheckError("Query failed 1", sql)) |
1914 | { | 2040 | { |
1915 | ret = dbCount(db, table, where); | 2041 | ret = dbCount(table, where); |
1916 | free(sql); | 2042 | free(sql); |
1917 | return ret; | 2043 | return ret; |
1918 | } | 2044 | } |
1919 | } | 2045 | } |
1920 | else | 2046 | else |
1921 | { | 2047 | { |
1922 | MYSQL_RES *result = mysql_store_result(db); | 2048 | MYSQL_RES *result = mysql_store_result(database); |
1923 | 2049 | ||
1924 | if (!result) | 2050 | if (!result) |
1925 | E("Couldn't get results set from %s\n: %s", sql, mysql_error(db)); | 2051 | E("Couldn't get results set from %s\n: %s", sql, mysql_error(database)); |
1926 | else | 2052 | else |
1927 | { | 2053 | { |
1928 | MYSQL_ROW row = mysql_fetch_row(result); | 2054 | MYSQL_ROW row = mysql_fetch_row(result); |
1929 | if (!row) | 2055 | if (!row) |
1930 | E("MariaDB error %d - Couldn't get row from %s\n: %s", mysql_errno(db), sql, mysql_error(db)); | 2056 | E("MariaDB error %d - Couldn't get row from %s\n: %s", mysql_errno(database), sql, mysql_error(database)); |
1931 | else | 2057 | else |
1932 | ret = atoll(row[0]); | 2058 | ret = atoll(row[0]); |
1933 | mysql_free_result(result); | 2059 | mysql_free_result(result); |
@@ -1943,7 +2069,7 @@ my_ulonglong dbCount(MYSQL *db, char *table, char *where) | |||
1943 | return ret; | 2069 | return ret; |
1944 | } | 2070 | } |
1945 | 2071 | ||
1946 | my_ulonglong dbCountJoin(MYSQL *db, char *table, char *select, char *join, char *where) | 2072 | my_ulonglong dbCountJoin(char *table, char *select, char *join, char *where) |
1947 | { | 2073 | { |
1948 | my_ulonglong ret = 0; | 2074 | my_ulonglong ret = 0; |
1949 | char *sql; | 2075 | char *sql; |
@@ -1962,22 +2088,22 @@ my_ulonglong dbCountJoin(MYSQL *db, char *table, char *select, char *join, char | |||
1962 | else | 2088 | else |
1963 | sql = xmprintf("SELECT %s FROM %s", select, table, join); | 2089 | sql = xmprintf("SELECT %s FROM %s", select, table, join); |
1964 | 2090 | ||
1965 | if (mysql_query(db, sql)) | 2091 | if (mysql_query(database, sql)) |
1966 | { | 2092 | { |
1967 | // E("MariaDB error %d - Query failed 2: %s", mysql_errno(db), mysql_error(db)); | 2093 | // E("MariaDB error %d - Query failed 2: %s", mysql_errno(database), mysql_error(database)); |
1968 | if (dbCheckError(db, "Query failed 2", sql)) | 2094 | if (dbCheckError("Query failed 2", sql)) |
1969 | { | 2095 | { |
1970 | ret = dbCountJoin(db, table, select, join, where); | 2096 | ret = dbCountJoin(table, select, join, where); |
1971 | free(sql); | 2097 | free(sql); |
1972 | return ret; | 2098 | return ret; |
1973 | } | 2099 | } |
1974 | } | 2100 | } |
1975 | else | 2101 | else |
1976 | { | 2102 | { |
1977 | MYSQL_RES *result = mysql_store_result(db); | 2103 | MYSQL_RES *result = mysql_store_result(database); |
1978 | 2104 | ||
1979 | if (!result) | 2105 | if (!result) |
1980 | E("MariaDB error %d - Couldn't get results set from %s\n: %s", mysql_errno(db), sql, mysql_error(db)); | 2106 | E("MariaDB error %d - Couldn't get results set from %s\n: %s", mysql_errno(database), sql, mysql_error(database)); |
1981 | else | 2107 | else |
1982 | ret = mysql_num_rows(result); | 2108 | ret = mysql_num_rows(result); |
1983 | mysql_free_result(result); | 2109 | mysql_free_result(result); |
@@ -1992,51 +2118,6 @@ my_ulonglong dbCountJoin(MYSQL *db, char *table, char *select, char *join, char | |||
1992 | return ret; | 2118 | return ret; |
1993 | } | 2119 | } |
1994 | 2120 | ||
1995 | MYSQL_RES *dbSelect(MYSQL *db, char *table, char *select, char *join, char *where, char *order) | ||
1996 | { | ||
1997 | MYSQL_RES *ret = NULL; | ||
1998 | char *sql; | ||
1999 | struct timespec now, then; | ||
2000 | |||
2001 | if (-1 == clock_gettime(CLOCK_REALTIME, &then)) | ||
2002 | perror_msg("Unable to get the time."); | ||
2003 | |||
2004 | if (NULL == select) | ||
2005 | select = "*"; | ||
2006 | if (NULL == join) | ||
2007 | join = ""; | ||
2008 | |||
2009 | if (where) | ||
2010 | sql = xmprintf("SELECT %s FROM %s %s WHERE %s", select, table, join, where); | ||
2011 | else | ||
2012 | sql = xmprintf("SELECT %s FROM %s", select, table, join); | ||
2013 | |||
2014 | if (order) | ||
2015 | { | ||
2016 | char *t = xmprintf("%s ORDER BY %s", sql, order); | ||
2017 | |||
2018 | free(sql); | ||
2019 | sql = t; | ||
2020 | } | ||
2021 | |||
2022 | if (mysql_query(db, sql)) | ||
2023 | E("MariaDB error %d - Query failed 3: %s\n%s", mysql_errno(db), mysql_error(db), sql); | ||
2024 | else | ||
2025 | { | ||
2026 | ret = mysql_store_result(db); | ||
2027 | if (!ret) | ||
2028 | E("MariaDB error %d - Couldn't get results set from %s\n %s", mysql_errno(db), mysql_error(db), sql); | ||
2029 | } | ||
2030 | |||
2031 | if (-1 == clock_gettime(CLOCK_REALTIME, &now)) | ||
2032 | perror_msg("Unable to get the time."); | ||
2033 | double n = (now.tv_sec * 1000000000.0) + now.tv_nsec; | ||
2034 | double t = (then.tv_sec * 1000000000.0) + then.tv_nsec; | ||
2035 | T("dbSelect(%s) took %lf seconds", sql, (n - t) / 1000000000.0); | ||
2036 | free(sql); | ||
2037 | return ret; | ||
2038 | } | ||
2039 | |||
2040 | 2121 | ||
2041 | void replaceStr(qhashtbl_t *ssi, char *key, char *value) | 2122 | void replaceStr(qhashtbl_t *ssi, char *key, char *value) |
2042 | { | 2123 | { |
@@ -2105,91 +2186,88 @@ gridStats *getStats(MYSQL *db, gridStats *stats) | |||
2105 | replaceStr(stats->stats, "gridOnline", "online"); | 2186 | replaceStr(stats->stats, "gridOnline", "online"); |
2106 | else | 2187 | else |
2107 | replaceStr(stats->stats, "gridOnline", "offline"); | 2188 | replaceStr(stats->stats, "gridOnline", "offline"); |
2108 | if (db) | ||
2109 | { | ||
2110 | char *tmp; | ||
2111 | my_ulonglong locIn = dbCount(db, "Presence", "RegionID != '00000000-0000-0000-0000-000000000000'"); // Locals online but not HGing, and HGers in world. | ||
2112 | my_ulonglong HGin = dbCount(db, "Presence", "UserID NOT IN (SELECT PrincipalID FROM UserAccounts)"); // HGers in world. | ||
2113 | 2189 | ||
2114 | // Collect stats about members. | 2190 | char *tmp; |
2115 | replaceLong(stats->stats, "hgers", HGin); | 2191 | my_ulonglong locIn = dbCount("Presence", "RegionID != '00000000-0000-0000-0000-000000000000'"); // Locals online but not HGing, and HGers in world. |
2116 | if (locIn >= HGin) // Does OpenSim have too many ghosts? | 2192 | my_ulonglong HGin = dbCount("Presence", "UserID NOT IN (SELECT PrincipalID FROM UserAccounts)"); // HGers in world. |
2117 | replaceLong(stats->stats, "inworld", locIn - HGin); | ||
2118 | else | ||
2119 | replaceLong(stats->stats, "inworld", 0); | ||
2120 | tmp = xmprintf("GridExternalName != '%s'", stats->stats->getstr(stats->stats, "uri", false)); | ||
2121 | replaceLong(stats->stats, "outworld", dbCount(db, "hg_traveling_data", tmp)); | ||
2122 | free(tmp); | ||
2123 | replaceLong(stats->stats, "members", dbCount(db, "UserAccounts", NULL)); | ||
2124 | |||
2125 | // Count local and HG visitors for the last 30 and 60 days. | ||
2126 | locIn = dbCountJoin(db, "GridUser", "GridUser.UserID", "INNER JOIN UserAccounts ON GridUser.UserID = UserAccounts.PrincipalID", | ||
2127 | "Login > UNIX_TIMESTAMP(FROM_UNIXTIME(UNIX_TIMESTAMP(now()) - 2419200))"); | ||
2128 | HGin = dbCount(db, "GridUser", "Login > UNIX_TIMESTAMP(FROM_UNIXTIME(UNIX_TIMESTAMP(now()) - 2419200))"); | ||
2129 | replaceLong(stats->stats, "locDay30", locIn); | ||
2130 | replaceLong(stats->stats, "day30", HGin); | ||
2131 | replaceLong(stats->stats, "HGday30", HGin - locIn); | ||
2132 | |||
2133 | locIn = dbCountJoin(db, "GridUser", "GridUser.UserID", "INNER JOIN UserAccounts ON GridUser.UserID = UserAccounts.PrincipalID", | ||
2134 | "Login > UNIX_TIMESTAMP(FROM_UNIXTIME(UNIX_TIMESTAMP(now()) - 4838400))"); | ||
2135 | HGin = dbCount(db, "GridUser", "Login > UNIX_TIMESTAMP(FROM_UNIXTIME(UNIX_TIMESTAMP(now()) - 4838400))"); | ||
2136 | replaceLong(stats->stats, "locDay60", locIn); | ||
2137 | replaceLong(stats->stats, "day60", HGin); | ||
2138 | replaceLong(stats->stats, "HGday60", HGin - locIn); | ||
2139 | |||
2140 | // Collect stats about sims. | ||
2141 | replaceLong(stats->stats, "sims", dbCount(db, "regions", NULL)); | ||
2142 | replaceLong(stats->stats, "onlineSims", dbCount(db, "regions", "sizeX != 0")); | ||
2143 | replaceLong(stats->stats, "varRegions", dbCount(db, "regions", "sizeX > 256 or sizeY > 256")); | ||
2144 | replaceLong(stats->stats, "singleSims", dbCount(db, "regions", "sizeX = 256 and sizeY = 256")); | ||
2145 | replaceLong(stats->stats, "offlineSims", dbCount(db, "regions", "sizeX = 0")); | ||
2146 | |||
2147 | // Calculate total size of all regions. | ||
2148 | my_ulonglong simSize = 0; | ||
2149 | static dbRequest *rgnSizes = NULL; | ||
2150 | if (NULL == rgnSizes) | ||
2151 | { | ||
2152 | static char *szi[] = {NULL}; | ||
2153 | static char *szo[] = {"sizeX", "sizeY", NULL}; | ||
2154 | rgnSizes = xzalloc(sizeof(dbRequest)); | ||
2155 | rgnSizes->db = db; | ||
2156 | rgnSizes->table = "regions"; | ||
2157 | rgnSizes->inParams = szi; | ||
2158 | rgnSizes->outParams = szo; | ||
2159 | rgnSizes->where = "sizeX != 0"; | ||
2160 | dbRequests->addfirst(dbRequests, &rgnSizes, sizeof(dbRequest *)); | ||
2161 | } | ||
2162 | dbDoSomething(rgnSizes, FALSE); | ||
2163 | rowData *rows = rgnSizes->rows; | ||
2164 | 2193 | ||
2165 | qhashtbl_t *row; | 2194 | // Collect stats about members. |
2166 | while (NULL != (row = rows->rows->getat(rows->rows, 0, NULL, true))) | 2195 | replaceLong(stats->stats, "hgers", HGin); |
2167 | { | 2196 | if (locIn >= HGin) // Does OpenSim have too many ghosts? |
2168 | my_ulonglong x = 0, y = 0; | 2197 | replaceLong(stats->stats, "inworld", locIn - HGin); |
2198 | else | ||
2199 | replaceLong(stats->stats, "inworld", 0); | ||
2200 | tmp = xmprintf("GridExternalName != '%s'", stats->stats->getstr(stats->stats, "uri", false)); | ||
2201 | replaceLong(stats->stats, "outworld", dbCount("hg_traveling_data", tmp)); | ||
2202 | free(tmp); | ||
2203 | replaceLong(stats->stats, "members", dbCount("UserAccounts", NULL)); | ||
2169 | 2204 | ||
2170 | tmp = row->getstr(row, "sizeX", false); | 2205 | // Count local and HG visitors for the last 30 and 60 days. |
2171 | if (NULL == tmp) | 2206 | locIn = dbCountJoin("GridUser", "GridUser.UserID", "INNER JOIN UserAccounts ON GridUser.UserID = UserAccounts.PrincipalID", |
2172 | E("No regions.sizeX!"); | 2207 | "Login > UNIX_TIMESTAMP(FROM_UNIXTIME(UNIX_TIMESTAMP(now()) - 2419200))"); |
2173 | else | 2208 | HGin = dbCount("GridUser", "Login > UNIX_TIMESTAMP(FROM_UNIXTIME(UNIX_TIMESTAMP(now()) - 2419200))"); |
2174 | x = atoll(tmp); | 2209 | replaceLong(stats->stats, "locDay30", locIn); |
2175 | tmp = row->getstr(row, "sizeY", false); | 2210 | replaceLong(stats->stats, "day30", HGin); |
2176 | if (NULL == tmp) | 2211 | replaceLong(stats->stats, "HGday30", HGin - locIn); |
2177 | E("No regions.sizeY!"); | ||
2178 | else | ||
2179 | y = atoll(tmp); | ||
2180 | simSize += x * y; | ||
2181 | row->free(row); | ||
2182 | rows->rows->removefirst(rows->rows); | ||
2183 | } | ||
2184 | free(rows->fieldNames); | ||
2185 | rows->rows->free(rows->rows); | ||
2186 | free(rows); | ||
2187 | 2212 | ||
2188 | tmp = xmprintf("%lu", simSize); | 2213 | locIn = dbCountJoin("GridUser", "GridUser.UserID", "INNER JOIN UserAccounts ON GridUser.UserID = UserAccounts.PrincipalID", |
2189 | stats->stats->putstr(stats->stats, "simsSize", tmp); | 2214 | "Login > UNIX_TIMESTAMP(FROM_UNIXTIME(UNIX_TIMESTAMP(now()) - 4838400))"); |
2190 | free(tmp); | 2215 | HGin = dbCount("GridUser", "Login > UNIX_TIMESTAMP(FROM_UNIXTIME(UNIX_TIMESTAMP(now()) - 4838400))"); |
2191 | gettimeofday(&(stats->last), NULL); | 2216 | replaceLong(stats->stats, "locDay60", locIn); |
2217 | replaceLong(stats->stats, "day60", HGin); | ||
2218 | replaceLong(stats->stats, "HGday60", HGin - locIn); | ||
2219 | |||
2220 | // Collect stats about sims. | ||
2221 | replaceLong(stats->stats, "sims", dbCount("regions", NULL)); | ||
2222 | replaceLong(stats->stats, "onlineSims", dbCount("regions", "sizeX != 0")); | ||
2223 | replaceLong(stats->stats, "varRegions", dbCount("regions", "sizeX > 256 or sizeY > 256")); | ||
2224 | replaceLong(stats->stats, "singleSims", dbCount("regions", "sizeX = 256 and sizeY = 256")); | ||
2225 | replaceLong(stats->stats, "offlineSims", dbCount("regions", "sizeX = 0")); | ||
2226 | |||
2227 | // Calculate total size of all regions. | ||
2228 | my_ulonglong simSize = 0; | ||
2229 | static dbRequest *rgnSizes = NULL; | ||
2230 | if (NULL == rgnSizes) | ||
2231 | { | ||
2232 | static char *szi[] = {NULL}; | ||
2233 | static char *szo[] = {"sizeX", "sizeY", NULL}; | ||
2234 | rgnSizes = xzalloc(sizeof(dbRequest)); | ||
2235 | rgnSizes->table = "regions"; | ||
2236 | rgnSizes->inParams = szi; | ||
2237 | rgnSizes->outParams = szo; | ||
2238 | rgnSizes->where = "sizeX != 0"; | ||
2239 | dbRequests->addfirst(dbRequests, &rgnSizes, sizeof(dbRequest *)); | ||
2240 | } | ||
2241 | dbDoSomething(rgnSizes, FALSE); // LEAKY | ||
2242 | rowData *rows = rgnSizes->rows; | ||
2243 | |||
2244 | qhashtbl_t *row; | ||
2245 | while (NULL != (row = rows->rows->getat(rows->rows, 0, NULL, true))) | ||
2246 | { | ||
2247 | my_ulonglong x = 0, y = 0; | ||
2248 | |||
2249 | tmp = row->getstr(row, "sizeX", false); | ||
2250 | if (NULL == tmp) | ||
2251 | E("No regions.sizeX!"); | ||
2252 | else | ||
2253 | x = atoll(tmp); | ||
2254 | tmp = row->getstr(row, "sizeY", false); | ||
2255 | if (NULL == tmp) | ||
2256 | E("No regions.sizeY!"); | ||
2257 | else | ||
2258 | y = atoll(tmp); | ||
2259 | simSize += x * y; | ||
2260 | row->free(row); | ||
2261 | rows->rows->removefirst(rows->rows); | ||
2192 | } | 2262 | } |
2263 | free(rows->fieldNames); | ||
2264 | rows->rows->free(rows->rows); | ||
2265 | free(rows); | ||
2266 | |||
2267 | tmp = xmprintf("%lu", simSize); | ||
2268 | stats->stats->putstr(stats->stats, "simsSize", tmp); | ||
2269 | free(tmp); | ||
2270 | gettimeofday(&(stats->last), NULL); | ||
2193 | 2271 | ||
2194 | return stats; | 2272 | return stats; |
2195 | } | 2273 | } |
@@ -3587,7 +3665,7 @@ static void generateAccountUUID(reqData *Rd) | |||
3587 | // Try database. | 3665 | // Try database. |
3588 | where = xmprintf("UserAccounts.PrincipalID = '%s'", uuid); | 3666 | where = xmprintf("UserAccounts.PrincipalID = '%s'", uuid); |
3589 | D("Trying new UUID %s.", where); | 3667 | D("Trying new UUID %s.", where); |
3590 | users = dbCount(Rd->db, "UserAccounts", where); | 3668 | users = dbCount("UserAccounts", where); |
3591 | free(where); | 3669 | free(where); |
3592 | } while (users != 0); | 3670 | } while (users != 0); |
3593 | if (NULL != Rd->shs.UUID) free(Rd->shs.UUID); | 3671 | if (NULL != Rd->shs.UUID) free(Rd->shs.UUID); |
@@ -3828,7 +3906,6 @@ notWritten: | |||
3828 | }; | 3906 | }; |
3829 | static char *szo[] = {NULL}; | 3907 | static char *szo[] = {NULL}; |
3830 | acntsI = xzalloc(sizeof(dbRequest)); | 3908 | acntsI = xzalloc(sizeof(dbRequest)); |
3831 | acntsI->db = Rd->db; | ||
3832 | acntsI->table = "UserAccounts"; | 3909 | acntsI->table = "UserAccounts"; |
3833 | acntsI->inParams = szi; | 3910 | acntsI->inParams = szi; |
3834 | acntsI->outParams = szo; | 3911 | acntsI->outParams = szo; |
@@ -3842,7 +3919,6 @@ notWritten: | |||
3842 | static char *szi[] = {"UUID", "passwordSalt", "passwordHash", "accountType", "webLoginKey", NULL}; | 3919 | static char *szi[] = {"UUID", "passwordSalt", "passwordHash", "accountType", "webLoginKey", NULL}; |
3843 | static char *szo[] = {NULL}; | 3920 | static char *szo[] = {NULL}; |
3844 | authI = xzalloc(sizeof(dbRequest)); | 3921 | authI = xzalloc(sizeof(dbRequest)); |
3845 | authI->db = Rd->db; | ||
3846 | authI->table = "auth"; | 3922 | authI->table = "auth"; |
3847 | authI->inParams = szi; | 3923 | authI->inParams = szi; |
3848 | authI->outParams = szo; | 3924 | authI->outParams = szo; |
@@ -3865,7 +3941,6 @@ notWritten: | |||
3865 | }; | 3941 | }; |
3866 | static char *szo[] = {NULL}; | 3942 | static char *szo[] = {NULL}; |
3867 | invFolderI = xzalloc(sizeof(dbRequest)); | 3943 | invFolderI = xzalloc(sizeof(dbRequest)); |
3868 | invFolderI->db = Rd->db; | ||
3869 | invFolderI->table = "inventoryfolders"; | 3944 | invFolderI->table = "inventoryfolders"; |
3870 | invFolderI->inParams = szi; | 3945 | invFolderI->inParams = szi; |
3871 | invFolderI->outParams = szo; | 3946 | invFolderI->outParams = szo; |
@@ -3880,7 +3955,6 @@ notWritten: | |||
3880 | static char *szi[] = {"UserID", NULL}; // All the defaults are what we would set anyway. | 3955 | static char *szi[] = {"UserID", NULL}; // All the defaults are what we would set anyway. |
3881 | static char *szo[] = {NULL}; | 3956 | static char *szo[] = {NULL}; |
3882 | gUserI = xzalloc(sizeof(dbRequest)); | 3957 | gUserI = xzalloc(sizeof(dbRequest)); |
3883 | gUserI->db = Rd->db; | ||
3884 | gUserI->table = "GridUser"; | 3958 | gUserI->table = "GridUser"; |
3885 | gUserI->inParams = szi; | 3959 | gUserI->inParams = szi; |
3886 | gUserI->outParams = szo; | 3960 | gUserI->outParams = szo; |
@@ -3922,7 +3996,7 @@ notWritten: | |||
3922 | uuid_unparse_lower(binuuidI, uuidI); | 3996 | uuid_unparse_lower(binuuidI, uuidI); |
3923 | // TODO - should check there isn't a folder with this UUID already. | 3997 | // TODO - should check there isn't a folder with this UUID already. |
3924 | D("Creating %s inventory folder for user %s.", sysFolders[i].name, getStrH(Rd->stuff, "name")); | 3998 | D("Creating %s inventory folder for user %s.", sysFolders[i].name, getStrH(Rd->stuff, "name")); |
3925 | r += dbDoSomething(invFolderI, FALSE, uuid, sysFolders[i].name, sysFolders[i].type, 1, uuidI, uuidR); | 3999 | r += dbDoSomething(invFolderI, FALSE, uuid, sysFolders[i].name, sysFolders[i].type, 1, uuidI, uuidR); // LEAKY |
3926 | if (0 != r) | 4000 | if (0 != r) |
3927 | bitch(Rd, "Internal error.", "Failed to create invenoryFolder record."); | 4001 | bitch(Rd, "Internal error.", "Failed to create invenoryFolder record."); |
3928 | if (strcmp("My Inventory", sysFolders[i].name) == 0) | 4002 | if (strcmp("My Inventory", sysFolders[i].name) == 0) |
@@ -3965,7 +4039,7 @@ notWritten: | |||
3965 | { | 4039 | { |
3966 | // Create location record. | 4040 | // Create location record. |
3967 | D("Creating home and last positions for user %s.", getStrH(Rd->stuff, "name")); | 4041 | D("Creating home and last positions for user %s.", getStrH(Rd->stuff, "name")); |
3968 | if (0 != dbDoSomething(gUserI, FALSE, uuid)) | 4042 | if (0 != dbDoSomething(gUserI, FALSE, uuid)) // LEAKY |
3969 | bitch(Rd, "Internal error.", "Failed to create GridUser record."); | 4043 | bitch(Rd, "Internal error.", "Failed to create GridUser record."); |
3970 | else | 4044 | else |
3971 | { | 4045 | { |
@@ -5055,7 +5129,6 @@ static int accountRead(reqData *Rd, char *uuid, char *firstName, char *lastName) | |||
5055 | static char *szi[] = {"PrincipalID", NULL}; | 5129 | static char *szi[] = {"PrincipalID", NULL}; |
5056 | static char *szo[] = {NULL}; | 5130 | static char *szo[] = {NULL}; |
5057 | uuids = xzalloc(sizeof(dbRequest)); | 5131 | uuids = xzalloc(sizeof(dbRequest)); |
5058 | uuids->db = Rd->db; | ||
5059 | uuids->table = "UserAccounts"; | 5132 | uuids->table = "UserAccounts"; |
5060 | uuids->inParams = szi; | 5133 | uuids->inParams = szi; |
5061 | uuids->outParams = szo; | 5134 | uuids->outParams = szo; |
@@ -5068,7 +5141,6 @@ static int accountRead(reqData *Rd, char *uuid, char *firstName, char *lastName) | |||
5068 | static char *szi[] = {"FirstName", "LastName", NULL}; | 5141 | static char *szi[] = {"FirstName", "LastName", NULL}; |
5069 | static char *szo[] = {NULL}; | 5142 | static char *szo[] = {NULL}; |
5070 | acnts = xzalloc(sizeof(dbRequest)); | 5143 | acnts = xzalloc(sizeof(dbRequest)); |
5071 | acnts->db = Rd->db; | ||
5072 | acnts->table = "UserAccounts"; | 5144 | acnts->table = "UserAccounts"; |
5073 | acnts->inParams = szi; | 5145 | acnts->inParams = szi; |
5074 | acnts->outParams = szo; | 5146 | acnts->outParams = szo; |
@@ -5081,7 +5153,6 @@ static int accountRead(reqData *Rd, char *uuid, char *firstName, char *lastName) | |||
5081 | static char *szi[] = {"UUID", NULL}; | 5153 | static char *szi[] = {"UUID", NULL}; |
5082 | static char *szo[] = {"passwordSalt", "passwordHash", NULL}; | 5154 | static char *szo[] = {"passwordSalt", "passwordHash", NULL}; |
5083 | auth = xzalloc(sizeof(dbRequest)); | 5155 | auth = xzalloc(sizeof(dbRequest)); |
5084 | auth->db = Rd->db; | ||
5085 | auth->table = "auth"; | 5156 | auth->table = "auth"; |
5086 | auth->inParams = szi; | 5157 | auth->inParams = szi; |
5087 | auth->outParams = szo; | 5158 | auth->outParams = szo; |
@@ -5145,7 +5216,7 @@ d("accountRead() UUID %s, name %s %s", uuid, first, last); | |||
5145 | rt = LuaToHash(Rd, where, "user", tnm, ret, &st, &now, "user"); | 5216 | rt = LuaToHash(Rd, where, "user", tnm, ret, &st, &now, "user"); |
5146 | 5217 | ||
5147 | free(where); | 5218 | free(where); |
5148 | dbDoSomething(acnts, FALSE, first, last); | 5219 | dbDoSomething(acnts, FALSE, first, last); // LEAKY |
5149 | rows = acnts->rows; | 5220 | rows = acnts->rows; |
5150 | } | 5221 | } |
5151 | } | 5222 | } |
@@ -5194,7 +5265,7 @@ T("Found database record."); | |||
5194 | Rd->fromDb = TRUE; | 5265 | Rd->fromDb = TRUE; |
5195 | Rd->database->putstr(Rd->database, "Lua.name", name); | 5266 | Rd->database->putstr(Rd->database, "Lua.name", name); |
5196 | free(name); | 5267 | free(name); |
5197 | dbDoSomething(auth, FALSE, getStrH(Rd->database, "UserAccounts.PrincipalID")); | 5268 | dbDoSomething(auth, FALSE, getStrH(Rd->database, "UserAccounts.PrincipalID")); // LEAKY |
5198 | rows = auth->rows; | 5269 | rows = auth->rows; |
5199 | if (rows) | 5270 | if (rows) |
5200 | { | 5271 | { |
@@ -6263,18 +6334,6 @@ static void cleanup(void) | |||
6263 | { | 6334 | { |
6264 | // TODO - not sure why, but this gets called twice on quitting sometimes. | 6335 | // TODO - not sure why, but this gets called twice on quitting sometimes. |
6265 | C("Caught signal, or quitting, cleaning up."); | 6336 | C("Caught signal, or quitting, cleaning up."); |
6266 | dbRequest **rq; | ||
6267 | |||
6268 | if (dbRequests) | ||
6269 | { | ||
6270 | while (NULL != (rq = (dbRequest **) dbRequests->popfirst(dbRequests, NULL))) | ||
6271 | { | ||
6272 | dbFreeRequest(*rq); | ||
6273 | free(rq); | ||
6274 | } | ||
6275 | dbRequests->free(dbRequests); | ||
6276 | dbRequests = NULL; | ||
6277 | } | ||
6278 | 6337 | ||
6279 | if (accountPages) | 6338 | if (accountPages) |
6280 | { | 6339 | { |
@@ -6326,9 +6385,7 @@ static void cleanup(void) | |||
6326 | } | 6385 | } |
6327 | if (mimeTypes) mimeTypes->free(mimeTypes); | 6386 | if (mimeTypes) mimeTypes->free(mimeTypes); |
6328 | mimeTypes = NULL; | 6387 | mimeTypes = NULL; |
6329 | if (database) mysql_close(database); | 6388 | freeDb(true); |
6330 | database = NULL; | ||
6331 | mysql_library_end(); | ||
6332 | if (L) lua_close(L); | 6389 | if (L) lua_close(L); |
6333 | L = NULL; | 6390 | L = NULL; |
6334 | if (stats) | 6391 | if (stats) |
@@ -6671,28 +6728,19 @@ jit library is loaded or the JIT compiler will not be activated. | |||
6671 | E("mysql_library_init() failed!"); | 6728 | E("mysql_library_init() failed!"); |
6672 | goto finished; | 6729 | goto finished; |
6673 | } | 6730 | } |
6674 | database = mysql_init(NULL); | 6731 | if (!dbConnect()) |
6675 | if (NULL == database) | ||
6676 | { | ||
6677 | E("mysql_init() failed - %s", mysql_error(database)); | ||
6678 | goto finished; | 6732 | goto finished; |
6679 | } | ||
6680 | else | ||
6681 | { | ||
6682 | if (!dbConnect()) | ||
6683 | goto finished; | ||
6684 | 6733 | ||
6685 | // Need to kick this off. | 6734 | // Need to kick this off. |
6686 | stats = getStats(database, stats); | 6735 | stats = getStats(database, stats); |
6687 | char *h = qstrunchar(qconfig->getstr(qconfig, "Const.HostName", false), '"', '"'); | 6736 | char *h = qstrunchar(qconfig->getstr(qconfig, "Const.HostName", false), '"', '"'); |
6688 | char *p = qstrunchar(qconfig->getstr(qconfig, "Const.PublicPort", false), '"', '"'); | 6737 | char *p = qstrunchar(qconfig->getstr(qconfig, "Const.PublicPort", false), '"', '"'); |
6689 | stats->stats->putstr(stats->stats, "grid", qstrunchar(qconfig->getstr(qconfig, "Const.GridName", false), '"', '"')); | 6738 | stats->stats->putstr(stats->stats, "grid", qstrunchar(qconfig->getstr(qconfig, "Const.GridName", false), '"', '"')); |
6690 | stats->stats->putstr(stats->stats, "HostName", h); | 6739 | stats->stats->putstr(stats->stats, "HostName", h); |
6691 | stats->stats->putstr(stats->stats, "PublicPort", p); | 6740 | stats->stats->putstr(stats->stats, "PublicPort", p); |
6692 | snprintf(toybuf, sizeof(toybuf), "http://%s:%s/", h, p); | 6741 | snprintf(toybuf, sizeof(toybuf), "http://%s:%s/", h, p); |
6693 | 6742 | ||
6694 | stats->stats->putstr(stats->stats, "uri", toybuf); | 6743 | stats->stats->putstr(stats->stats, "uri", toybuf); |
6695 | } | ||
6696 | qconfig->free(qconfig); | 6744 | qconfig->free(qconfig); |
6697 | } | 6745 | } |
6698 | 6746 | ||
@@ -6746,7 +6794,6 @@ jit library is loaded or the JIT compiler will not be activated. | |||
6746 | Rd->database = qhashtbl(0, 0); | 6794 | Rd->database = qhashtbl(0, 0); |
6747 | Rd->Rcookies = qhashtbl(0, 0); | 6795 | Rd->Rcookies = qhashtbl(0, 0); |
6748 | Rd->Rheaders = qhashtbl(0, 0); | 6796 | Rd->Rheaders = qhashtbl(0, 0); |
6749 | Rd->db = database; | ||
6750 | Rd->stats = stats; | 6797 | Rd->stats = stats; |
6751 | Rd->errors = qlist(0); | 6798 | Rd->errors = qlist(0); |
6752 | Rd->messages = qlist(0); | 6799 | Rd->messages = qlist(0); |