diff options
Diffstat (limited to 'src/sledjchisl.c')
-rw-r--r-- | src/sledjchisl.c | 1297 |
1 files changed, 0 insertions, 1297 deletions
diff --git a/src/sledjchisl.c b/src/sledjchisl.c deleted file mode 100644 index df24ad7..0000000 --- a/src/sledjchisl.c +++ /dev/null | |||
@@ -1,1297 +0,0 @@ | |||
1 | /* sledjchisl.c - opensim-SC management system. | ||
2 | * | ||
3 | * Copyright 2020 David Seikel <sledjchisl@sledjhamr.org> | ||
4 | */ | ||
5 | |||
6 | #include <fcgi_config.h> | ||
7 | #ifdef _WIN32 | ||
8 | #include <process.h> | ||
9 | #else | ||
10 | extern char **environ; | ||
11 | #endif | ||
12 | #define NO_FCGI_DEFINES | ||
13 | #include <fcgi_stdio.h> | ||
14 | #undef NO_FCGI_DEFINES | ||
15 | //#include "fcgiapp.h" | ||
16 | |||
17 | #include <lua.h> | ||
18 | #include <lualib.h> | ||
19 | #include <lauxlib.h> | ||
20 | #include <luajit.h> | ||
21 | |||
22 | #include "fcgi_SC.h" | ||
23 | #include "handlekeys.h" | ||
24 | |||
25 | // Both my_config.h and fcgi_config.h define the same PACKAGE* variables, which we don't use anyway, soooo - | ||
26 | //#undef PACKAGE | ||
27 | //#undef PACKAGE_NAME | ||
28 | //#undef PACKAGE_STRING | ||
29 | //#undef PACKAGE_TARNAME | ||
30 | //#undef PACKAGE_VERSION | ||
31 | //#undef VERSION | ||
32 | |||
33 | // https://mariadb.com/kb/en/about-mariadb-connector-c/ Official docs. | ||
34 | // http://dev.mysql.com/doc/refman/5.5/en/c-api-function-overview.html MySQL docs. | ||
35 | // http://zetcode.com/db/mysqlc/ MySQL tutorial. | ||
36 | #include <my_global.h> | ||
37 | #include <mysql.h> | ||
38 | |||
39 | #include <qlibc.h> | ||
40 | #include <extensions/qconfig.h> | ||
41 | |||
42 | // Toybox's library has something really odd in it that causes MariaDB library to crash, no idea what. | ||
43 | // I've copied the stuff I'm using, and that works. | ||
44 | #include "toybox.h" | ||
45 | |||
46 | |||
47 | //struct toy_context toys; | ||
48 | char toybuf[4096]; | ||
49 | int isTmux = 0; | ||
50 | int isWeb = 0; | ||
51 | char *pwd = ""; | ||
52 | char *scRoot = "/opt/opensim_SC"; | ||
53 | char *scUser = "opensimsc"; | ||
54 | char *Tconsole = "SledjChisl"; | ||
55 | char *Tsocket = "caches/opensim-tmux.socket"; | ||
56 | char *Ttab = "SC"; | ||
57 | char *Tcmd = "tmux -S"; | ||
58 | char *webRoot = "/opt/opensim_SC/web"; | ||
59 | float loadAverageInc = 0.5; | ||
60 | int simTimeOut = 45; | ||
61 | |||
62 | char *logTypes[] = | ||
63 | { | ||
64 | "CRITICAL", | ||
65 | "ERROR", | ||
66 | "WARNING", | ||
67 | "TIMEOUT", | ||
68 | "INFO", | ||
69 | "DEBUG", | ||
70 | }; | ||
71 | |||
72 | #define DATE_TIME_LEN 42 | ||
73 | void logMe(int v, char *format, ...) | ||
74 | { | ||
75 | va_list va, va2; | ||
76 | int len; | ||
77 | char *ret; | ||
78 | struct timeval tv; | ||
79 | time_t curtime; | ||
80 | char date[DATE_TIME_LEN]; | ||
81 | |||
82 | va_start(va, format); | ||
83 | va_copy(va2, va); | ||
84 | // How long is it? | ||
85 | len = vsnprintf(0, 0, format, va); | ||
86 | len++; | ||
87 | va_end(va); | ||
88 | // Allocate and do the sprintf() | ||
89 | ret = xmalloc(len); | ||
90 | vsnprintf(ret, len, format, va2); | ||
91 | va_end(va2); | ||
92 | |||
93 | gettimeofday(&tv, NULL); | ||
94 | curtime = tv.tv_sec; | ||
95 | strftime(date, DATE_TIME_LEN, "(%Z %z) %F %R", localtime(&curtime)); | ||
96 | |||
97 | fprintf(stderr, "%s.%.6ld %s: %s\n", date, tv.tv_usec, logTypes[v], ret); | ||
98 | free(ret); | ||
99 | } | ||
100 | #define C(...) logMe(0, __VA_ARGS__) | ||
101 | #define E(...) logMe(1, __VA_ARGS__) | ||
102 | #define W(...) logMe(2, __VA_ARGS__) | ||
103 | #define T(...) logMe(3, __VA_ARGS__) | ||
104 | #define I(...) logMe(4, __VA_ARGS__) | ||
105 | #define D(...) logMe(5, __VA_ARGS__) | ||
106 | |||
107 | |||
108 | |||
109 | // In Lua 5.0 reference manual is a table traversal example at page 29. | ||
110 | void PrintTable(lua_State *L) | ||
111 | { | ||
112 | lua_pushnil(L); | ||
113 | |||
114 | while (lua_next(L, -2) != 0) | ||
115 | { | ||
116 | // Numbers can convert to strings, so check for numbers before checking for strings. | ||
117 | if (lua_isnumber(L, -1)) | ||
118 | printf("%s = %f\n", lua_tostring(L, -2), lua_tonumber(L, -1)); | ||
119 | else if (lua_isstring(L, -1)) | ||
120 | printf("%s = '%s'\n", lua_tostring(L, -2), lua_tostring(L, -1)); | ||
121 | else if (lua_istable(L, -1)) | ||
122 | PrintTable(L); | ||
123 | lua_pop(L, 1); | ||
124 | } | ||
125 | } | ||
126 | |||
127 | |||
128 | int sendTmuxKeys(char *dest, char *keys) | ||
129 | { | ||
130 | int ret = 0, i; | ||
131 | char *c = xmprintf("%s %s/%s send-keys -t %s:%s '%s'", Tcmd, scRoot, Tsocket, Tconsole, dest, keys); | ||
132 | |||
133 | i = system(c); | ||
134 | if (!WIFEXITED(i)) | ||
135 | E("tmux send-keys command failed!"); | ||
136 | free(c); | ||
137 | return ret; | ||
138 | } | ||
139 | |||
140 | int sendTmuxCmd(char *dest, char *cmd) | ||
141 | { | ||
142 | int ret = 0, i; | ||
143 | char *c = xmprintf("%s %s/%s send-keys -t %s:'%s' '%s' Enter", Tcmd, scRoot, Tsocket, Tconsole, dest, cmd); | ||
144 | |||
145 | i = system(c); | ||
146 | if (!WIFEXITED(i)) | ||
147 | E("tmux send-keys command failed!"); | ||
148 | free(c); | ||
149 | return ret; | ||
150 | } | ||
151 | |||
152 | void waitTmuxText(char *dest, char *text) | ||
153 | { | ||
154 | int i; | ||
155 | char *c = xmprintf("sleep 5; %s %s/%s capture-pane -t %s:'%s' -p | grep -E '%s' 2>&1 > /dev/null", Tcmd, scRoot, Tsocket, Tconsole, dest, text); | ||
156 | |||
157 | D("Waiting for '%s'.", text); | ||
158 | do | ||
159 | { | ||
160 | i = system(c); | ||
161 | if (!WIFEXITED(i)) | ||
162 | { | ||
163 | E("tmux capture-pane command failed!"); | ||
164 | break; | ||
165 | } | ||
166 | else if (0 == WEXITSTATUS(i)) | ||
167 | break; | ||
168 | } while (1); | ||
169 | |||
170 | free(c); | ||
171 | } | ||
172 | |||
173 | float waitLoadAverage(float la, float extra, int timeout) | ||
174 | { | ||
175 | struct sysinfo info; | ||
176 | struct timespec timeOut; | ||
177 | float l; | ||
178 | int to = timeout; | ||
179 | |||
180 | I("Sleeping until load average is below %.02f (%.02f + %.02f) or for %d seconds.", la + extra, la, extra, timeout); | ||
181 | clock_gettime(CLOCK_MONOTONIC, &timeOut); | ||
182 | to += timeOut.tv_sec; | ||
183 | |||
184 | do | ||
185 | { | ||
186 | msleep(5000); | ||
187 | sysinfo(&info); | ||
188 | l = info.loads[0]/65536.0; | ||
189 | clock_gettime(CLOCK_MONOTONIC, &timeOut); | ||
190 | timeout -= 5; | ||
191 | I("Tick, load average is %.02f, countdown %d seconds.", l, timeout); | ||
192 | } while (((la + extra) < l) && (timeOut.tv_sec < to)); | ||
193 | |||
194 | return l; | ||
195 | } | ||
196 | |||
197 | |||
198 | typedef struct _simList simList; | ||
199 | struct _simList | ||
200 | { | ||
201 | int len, num; | ||
202 | char **sims; | ||
203 | }; | ||
204 | |||
205 | static int filterSims(struct tb_dirtree *node) | ||
206 | { | ||
207 | if (!node->parent) return TB_DIRTREE_RECURSE | TB_DIRTREE_SHUTUP; | ||
208 | if ((strncmp(node->name, "sim", 3) == 0) && ((strcmp(node->name, "sim_skeleton") != 0))) | ||
209 | { | ||
210 | simList *list = (simList *) node->parent->extra; | ||
211 | |||
212 | if ((list->num + 1) > list->len) | ||
213 | { | ||
214 | list->len = list->len + 1; | ||
215 | list->sims = xrealloc(list->sims, list->len * sizeof(char *)); | ||
216 | } | ||
217 | list->sims[list->num] = xstrdup(node->name); | ||
218 | list->num++; | ||
219 | } | ||
220 | return 0; | ||
221 | } | ||
222 | |||
223 | simList *getSims() | ||
224 | { | ||
225 | simList *sims = xmalloc(sizeof(simList)); | ||
226 | memset(sims, 0, sizeof(simList)); | ||
227 | memset(toybuf, 0, sizeof(toybuf)); | ||
228 | snprintf(toybuf, sizeof(toybuf), "%s/config", scRoot); | ||
229 | struct tb_dirtree *new = tb_dirtree_add_node(0, toybuf, 0); | ||
230 | new->extra = (long) sims; | ||
231 | tb_dirtree_handle_callback(new, filterSims); | ||
232 | qsort(sims->sims, sims->num, sizeof(char *), qstrcmp); | ||
233 | tb_dirtree_free(new); | ||
234 | return sims; | ||
235 | } | ||
236 | |||
237 | |||
238 | static int filterInis(struct tb_dirtree *node) | ||
239 | { | ||
240 | if (!node->parent) return TB_DIRTREE_RECURSE | TB_DIRTREE_SHUTUP | TB_DIRTREE_SAVE; | ||
241 | int l = strlen(node->name); | ||
242 | if (strncmp(&(node->name[l - 4]), ".ini", 4) == 0) | ||
243 | { | ||
244 | node->parent->extra = (long) node->name; | ||
245 | return TB_DIRTREE_ABORT; | ||
246 | } | ||
247 | return 0; | ||
248 | } | ||
249 | |||
250 | char *getSimName(char *sim) | ||
251 | { | ||
252 | char *ret = NULL; | ||
253 | char *c = xmprintf("%s/config/%s", scRoot, sim); | ||
254 | struct tb_dirtree *new = tb_dirtree_add_node(0, c, 0); | ||
255 | |||
256 | tb_dirtree_handle_callback(new, filterInis); | ||
257 | if (new->extra) | ||
258 | { | ||
259 | char *temp = NULL; | ||
260 | regex_t pat; | ||
261 | regmatch_t m[2]; | ||
262 | long len; | ||
263 | int fd; | ||
264 | |||
265 | c = xmprintf("%s/config/%s/%s", scRoot, sim, new->extra); | ||
266 | fd = xopenro(c); | ||
267 | xregcomp(&pat, "RegionName = \"(.+)\"", REG_EXTENDED); | ||
268 | do | ||
269 | { | ||
270 | // TODO - get_line() is slow, and wont help much with DOS and Mac line endings. | ||
271 | temp = get_line(fd); | ||
272 | if (temp) | ||
273 | { | ||
274 | if (!regexec(&pat, temp, 2, m, 0)) | ||
275 | { | ||
276 | // Return first parenthesized subexpression as string. | ||
277 | if (pat.re_nsub > 0) | ||
278 | { | ||
279 | ret = xmprintf("%.*s", (int) (m[1].rm_eo - m[1].rm_so), temp + m[1].rm_so); | ||
280 | break; | ||
281 | } | ||
282 | } | ||
283 | } | ||
284 | } while (temp); | ||
285 | xclose(fd); | ||
286 | } | ||
287 | tb_dirtree_free(new); | ||
288 | return ret; | ||
289 | } | ||
290 | |||
291 | |||
292 | // Expects either "simXX" or "ROBUST". | ||
293 | int checkSimIsRunning(char *sim) | ||
294 | { | ||
295 | int ret = 0; | ||
296 | struct stat st; | ||
297 | |||
298 | // Check if it's running. | ||
299 | memset(toybuf, 0, sizeof(toybuf)); | ||
300 | snprintf(toybuf, sizeof(toybuf), "%s/caches/%s.pid", scRoot, sim); | ||
301 | if (0 == stat(toybuf, &st)) | ||
302 | { | ||
303 | int fd, i; | ||
304 | char *pid = NULL; | ||
305 | |||
306 | // Double check if it's REALLY running. | ||
307 | if ((fd = xopenro(toybuf)) == -1) | ||
308 | tb_perror_msg("xopenro(%s)", toybuf); | ||
309 | else | ||
310 | { | ||
311 | pid = get_line(fd); | ||
312 | if (NULL == pid) | ||
313 | tb_perror_msg("get_line(%s)", toybuf); | ||
314 | else | ||
315 | { | ||
316 | xclose(fd); | ||
317 | |||
318 | memset(toybuf, 0, sizeof(toybuf)); | ||
319 | snprintf(toybuf, sizeof(toybuf), "ps -p %s --no-headers -o comm", pid); | ||
320 | i = system(toybuf); | ||
321 | if (WIFEXITED(i)) | ||
322 | { | ||
323 | if (0 != WEXITSTATUS(i)) // No such pid. | ||
324 | { | ||
325 | memset(toybuf, 0, sizeof(toybuf)); | ||
326 | snprintf(toybuf, sizeof(toybuf), "rm -f %s/caches/%s.pid", scRoot, sim); | ||
327 | D("%s", toybuf); | ||
328 | i = system(toybuf); | ||
329 | } | ||
330 | } | ||
331 | } | ||
332 | } | ||
333 | } | ||
334 | |||
335 | // Now check if it's really really running. lol | ||
336 | memset(toybuf, 0, sizeof(toybuf)); | ||
337 | snprintf(toybuf, sizeof(toybuf), "%s/caches/%s.pid", scRoot, sim); | ||
338 | if (0 == stat(toybuf, &st)) | ||
339 | ret = 1; | ||
340 | |||
341 | return ret; | ||
342 | } | ||
343 | |||
344 | static void PrintEnv(char *label, char **envp) | ||
345 | { | ||
346 | FCGI_printf("%s:<br>\n<pre>\n", label); | ||
347 | for ( ; *envp != NULL; envp++) | ||
348 | { | ||
349 | FCGI_printf("%s\n", *envp); | ||
350 | } | ||
351 | FCGI_printf("</pre><p>\n"); | ||
352 | } | ||
353 | |||
354 | static void printEnv(char **envp) | ||
355 | { | ||
356 | for ( ; *envp != NULL; envp++) | ||
357 | { | ||
358 | D("%s", *envp); | ||
359 | } | ||
360 | } | ||
361 | |||
362 | |||
363 | my_ulonglong dbCount(MYSQL *db, char *table, char *where) | ||
364 | { | ||
365 | my_ulonglong ret = 0; | ||
366 | |||
367 | memset(toybuf, 0, sizeof(toybuf)); | ||
368 | if (NULL == where) | ||
369 | snprintf(toybuf, sizeof(toybuf), "SELECT Count(*) FROM %s", table); | ||
370 | else | ||
371 | snprintf(toybuf, sizeof(toybuf), "SELECT Count(*) FROM %s WHERE %s", table, where); | ||
372 | |||
373 | if (mysql_query(db, toybuf)) | ||
374 | E("Query failed: %s", mysql_error(db)); | ||
375 | else | ||
376 | { | ||
377 | MYSQL_RES *result = mysql_store_result(db); | ||
378 | |||
379 | if (!result) | ||
380 | E("Couldn't get results set from %s\n: %s", toybuf, mysql_error(db)); | ||
381 | else | ||
382 | { | ||
383 | MYSQL_ROW row = mysql_fetch_row(result); | ||
384 | if (!row) | ||
385 | E("Couldn't get row from %s\n: %s", toybuf, mysql_error(db)); | ||
386 | else | ||
387 | ret = atoll(row[0]); | ||
388 | mysql_free_result(result); | ||
389 | } | ||
390 | } | ||
391 | |||
392 | return ret; | ||
393 | } | ||
394 | |||
395 | my_ulonglong dbCountJoin(MYSQL *db, char *table, char *select, char *join, char *where, char *order) | ||
396 | { | ||
397 | my_ulonglong ret = 0; | ||
398 | |||
399 | if (NULL == select) | ||
400 | select = "*"; | ||
401 | |||
402 | memset(toybuf, 0, sizeof(toybuf)); | ||
403 | if (NULL == where) | ||
404 | snprintf(toybuf, sizeof(toybuf), "SELECT %s FROM %s", select, table, join); | ||
405 | else | ||
406 | snprintf(toybuf, sizeof(toybuf), "SELECT %s FROM %s %s WHERE %s", select, table, join, where); | ||
407 | |||
408 | if (mysql_query(db, toybuf)) | ||
409 | E("Query failed: %s", mysql_error(db)); | ||
410 | else | ||
411 | { | ||
412 | MYSQL_RES *result = mysql_store_result(db); | ||
413 | |||
414 | if (!result) | ||
415 | E("Couldn't get results set from %s\n: %s", toybuf, mysql_error(db)); | ||
416 | else | ||
417 | ret = mysql_num_rows(result); | ||
418 | mysql_free_result(result); | ||
419 | } | ||
420 | |||
421 | return ret; | ||
422 | } | ||
423 | |||
424 | qlist_t *dbSelect(MYSQL *db, char *table, char *select, char *join, char *where, char *order) | ||
425 | { | ||
426 | qlist_t *ret = qlist(0); | ||
427 | if (NULL == select) | ||
428 | select = "*"; | ||
429 | |||
430 | memset(toybuf, 0, sizeof(toybuf)); | ||
431 | if (NULL == where) | ||
432 | snprintf(toybuf, sizeof(toybuf), "SELECT %s FROM %s", select, table, join); | ||
433 | else | ||
434 | snprintf(toybuf, sizeof(toybuf), "SELECT %s FROM %s %s WHERE %s", select, table, join, where); | ||
435 | |||
436 | if (mysql_query(db, toybuf)) | ||
437 | E("Query failed: %s", mysql_error(db)); | ||
438 | else | ||
439 | { | ||
440 | MYSQL_RES *result = mysql_store_result(db); | ||
441 | |||
442 | if (!result) | ||
443 | E("Couldn't get results set from %s\n: %s", toybuf, mysql_error(db)); | ||
444 | else | ||
445 | { | ||
446 | MYSQL_FIELD *fields; | ||
447 | |||
448 | fields = mysql_fetch_fields(result); | ||
449 | |||
450 | if (!fields) | ||
451 | E("Faild fetching fields: %s", mysql_error(db)); | ||
452 | else | ||
453 | { | ||
454 | unsigned int i, num_fields = mysql_num_fields(result); | ||
455 | |||
456 | MYSQL_ROW row; | ||
457 | |||
458 | while ((row = mysql_fetch_row(result))) | ||
459 | { | ||
460 | qhashtbl_t *flds = qhashtbl(0, 0); | ||
461 | |||
462 | for (i = 0; i < num_fields; i++) | ||
463 | { | ||
464 | flds->putstr(flds, fields[i].name, row[i]); | ||
465 | } | ||
466 | ret->addlast(ret, flds, sizeof(*flds)); | ||
467 | } | ||
468 | } | ||
469 | } | ||
470 | mysql_free_result(result); | ||
471 | } | ||
472 | |||
473 | return ret; | ||
474 | } | ||
475 | |||
476 | void replaceStr(qhashtbl_t *ssi, char *key, char *value) | ||
477 | { | ||
478 | // char *tmp; | ||
479 | |||
480 | // I think this is taken care of already. | ||
481 | // if ((tmp = ssi->getstr(ssi, key, false)) != NULL) | ||
482 | // free(tmp); | ||
483 | ssi->putstr(ssi, key, value); | ||
484 | } | ||
485 | |||
486 | void replaceLong(qhashtbl_t *ssi, char *key, my_ulonglong value) | ||
487 | { | ||
488 | char *tmp = xmprintf("%lu", value); | ||
489 | |||
490 | replaceStr(ssi, key, tmp); | ||
491 | free(tmp); | ||
492 | } | ||
493 | |||
494 | |||
495 | float timeDiff(struct timeval *now, struct timeval *then) | ||
496 | { | ||
497 | if (0 == gettimeofday(now, NULL)) | ||
498 | { | ||
499 | struct timeval thisTime = { 0, 0 }; | ||
500 | double result = 0.0; | ||
501 | |||
502 | thisTime.tv_sec = now->tv_sec; | ||
503 | thisTime.tv_usec = now->tv_usec; | ||
504 | if (thisTime.tv_usec < then->tv_usec) | ||
505 | { | ||
506 | thisTime.tv_sec--; | ||
507 | thisTime.tv_usec += 1000000; | ||
508 | } | ||
509 | thisTime.tv_usec -= then->tv_usec; | ||
510 | thisTime.tv_sec -= then->tv_sec; | ||
511 | result = ((double) thisTime.tv_usec) / ((double) 1000000.0); | ||
512 | result += thisTime.tv_sec; | ||
513 | return result; | ||
514 | } | ||
515 | else | ||
516 | return 0.0; | ||
517 | } | ||
518 | |||
519 | typedef struct _gridStats gridStats; | ||
520 | struct _gridStats | ||
521 | { | ||
522 | float next; | ||
523 | struct timeval last; | ||
524 | qhashtbl_t *stats; | ||
525 | }; | ||
526 | |||
527 | gridStats *getStats(MYSQL *db, gridStats *stats) | ||
528 | { | ||
529 | if (NULL == stats) | ||
530 | { | ||
531 | stats = xmalloc(sizeof(gridStats)); | ||
532 | stats->next = 300; | ||
533 | gettimeofday(&(stats->last), NULL); | ||
534 | stats->stats = qhashtbl(0, 0); | ||
535 | stats->stats->putstr(stats->stats, "version", "SledjChisl FCGI Dev 0.1"); | ||
536 | stats->stats->putstr(stats->stats, "grid", "my grid"); | ||
537 | stats->stats->putstr(stats->stats, "uri", "http://localhost:8002/"); | ||
538 | |||
539 | stats->stats->putstr(stats->stats, "gridOnline", "??"); | ||
540 | } | ||
541 | else | ||
542 | { | ||
543 | static struct timeval thisTime; | ||
544 | if (stats->next > timeDiff(&thisTime, &(stats->last))) | ||
545 | return stats; | ||
546 | } | ||
547 | |||
548 | if (db) | ||
549 | { | ||
550 | I("Getting fresh grid stats."); | ||
551 | char *tmp; | ||
552 | my_ulonglong locIn = dbCount(db, "Presence", "RegionID != '00000000-0000-0000-0000-000000000000'"); // Locals online but not HGing, and HGers in world. | ||
553 | my_ulonglong HGin = dbCount(db, "Presence", "UserID NOT IN (SELECT PrincipalID FROM UserAccounts)"); // HGers in world. | ||
554 | |||
555 | // Collect stats about members. | ||
556 | replaceLong(stats->stats, "hgers", HGin); | ||
557 | replaceLong(stats->stats, "inworld", locIn - HGin); | ||
558 | tmp = xmprintf("GridExternalName != '%s'", stats->stats->getstr(stats->stats, "uri", false)); | ||
559 | replaceLong(stats->stats, "outworld", dbCount(db, "hg_traveling_data", tmp)); | ||
560 | free(tmp); | ||
561 | replaceLong(stats->stats, "members", dbCount(db, "UserAccounts", NULL)); | ||
562 | |||
563 | // Count local and HG visitors for the last 30 and 60 days. | ||
564 | locIn = dbCountJoin(db, "GridUser", "GridUser.UserID", "INNER JOIN UserAccounts ON GridUser.UserID = UserAccounts.PrincipalID", | ||
565 | "Login > UNIX_TIMESTAMP(FROM_UNIXTIME(UNIX_TIMESTAMP(now()) - 2419200))", ""); | ||
566 | HGin = dbCount(db, "GridUser", "Login > UNIX_TIMESTAMP(FROM_UNIXTIME(UNIX_TIMESTAMP(now()) - 2419200))"); | ||
567 | replaceLong(stats->stats, "locDay30", locIn); | ||
568 | replaceLong(stats->stats, "day30", HGin); | ||
569 | replaceLong(stats->stats, "HGday30", HGin - locIn); | ||
570 | |||
571 | locIn = dbCountJoin(db, "GridUser", "GridUser.UserID", "INNER JOIN UserAccounts ON GridUser.UserID = UserAccounts.PrincipalID", | ||
572 | "Login > UNIX_TIMESTAMP(FROM_UNIXTIME(UNIX_TIMESTAMP(now()) - 4838400))", ""); | ||
573 | HGin = dbCount(db, "GridUser", "Login > UNIX_TIMESTAMP(FROM_UNIXTIME(UNIX_TIMESTAMP(now()) - 4838400))"); | ||
574 | replaceLong(stats->stats, "locDay60", locIn); | ||
575 | replaceLong(stats->stats, "day60", HGin); | ||
576 | replaceLong(stats->stats, "HGday60", HGin - locIn); | ||
577 | |||
578 | // Collect stats about sims. | ||
579 | replaceLong(stats->stats, "sims", dbCount(db, "regions", NULL)); | ||
580 | replaceLong(stats->stats, "onlineSims", dbCount(db, "regions", "sizeX != 0")); | ||
581 | replaceLong(stats->stats, "varRegions", dbCount(db, "regions", "sizeX > 256 or sizeY > 256")); | ||
582 | replaceLong(stats->stats, "singleSims", dbCount(db, "regions", "sizeX = 256 and sizeY = 256")); | ||
583 | replaceLong(stats->stats, "offlineSims", dbCount(db, "regions", "sizeX = 0")); | ||
584 | |||
585 | // Calculate total size of all regions. | ||
586 | qlist_t *regions = dbSelect(db, "regions", "sizeX,sizeY", "", "sizeX != 0", ""); | ||
587 | qlist_obj_t obj; | ||
588 | my_ulonglong simSize = 0; | ||
589 | |||
590 | memset((void *) &obj, 0, sizeof(obj)); // must be cleared before call | ||
591 | regions->lock(regions); | ||
592 | while (regions->getnext(regions, &obj, false) == true) | ||
593 | { | ||
594 | qhashtbl_t *row = (qhashtbl_t *) obj.data; | ||
595 | my_ulonglong x = 0, y = 0; | ||
596 | |||
597 | tmp = row->getstr(row, "sizeX", false); | ||
598 | if (NULL == tmp) | ||
599 | E("No regions.sizeX!"); | ||
600 | else | ||
601 | x = atoll(tmp); | ||
602 | tmp = row->getstr(row, "sizeY", false); | ||
603 | if (NULL == tmp) | ||
604 | E("No regions.sizeY!"); | ||
605 | else | ||
606 | y = atoll(tmp); | ||
607 | simSize += x * y; | ||
608 | } | ||
609 | regions->unlock(regions); | ||
610 | tmp = xmprintf("%lu", simSize); | ||
611 | stats->stats->putstr(stats->stats, "simsSize", tmp); | ||
612 | free(tmp); | ||
613 | gettimeofday(&(stats->last), NULL); | ||
614 | } | ||
615 | return stats; | ||
616 | } | ||
617 | |||
618 | |||
619 | enum fragmentType | ||
620 | { | ||
621 | FT_TEXT, | ||
622 | FT_PARAM, | ||
623 | FT_LUA | ||
624 | }; | ||
625 | |||
626 | typedef struct _fragment fragment; | ||
627 | struct _fragment | ||
628 | { | ||
629 | enum fragmentType type; | ||
630 | int length; | ||
631 | char *text; | ||
632 | }; | ||
633 | |||
634 | typedef struct _HTMLfile HTMLfile; | ||
635 | struct _HTMLfile | ||
636 | { | ||
637 | struct timespec last; | ||
638 | qlist_t *fragments; | ||
639 | }; | ||
640 | |||
641 | qhashtbl_t *HTMLfileCache = NULL; | ||
642 | |||
643 | fragment *newFragment(enum fragmentType type, char *text, int len) | ||
644 | { | ||
645 | fragment *frg = xmalloc(sizeof(fragment)); | ||
646 | frg->type = type; | ||
647 | frg->length = len; | ||
648 | frg->text = xmalloc(len + 1); | ||
649 | memcpy(frg->text, text, len); | ||
650 | frg->text[len] = '\0'; | ||
651 | return frg; | ||
652 | } | ||
653 | |||
654 | HTMLfile *checkHTMLcache(char *file) | ||
655 | { | ||
656 | if (NULL == HTMLfileCache) | ||
657 | HTMLfileCache = qhashtbl(0, 0); | ||
658 | |||
659 | HTMLfile *ret = (HTMLfile *) HTMLfileCache->get(HTMLfileCache, file, NULL, false); | ||
660 | int fd = open(file, O_RDONLY); | ||
661 | size_t length = 0; | ||
662 | fragment *frg0, *frg1; | ||
663 | |||
664 | if (-1 == fd) | ||
665 | E("Failed to open %s", file); | ||
666 | else | ||
667 | { | ||
668 | struct stat sb; | ||
669 | if (fstat(fd, &sb) == -1) | ||
670 | E("Failed to stat %s", file); | ||
671 | else | ||
672 | { | ||
673 | if ((NULL != ret) && (ret->last.tv_sec < sb.st_mtim.tv_sec)) | ||
674 | { | ||
675 | HTMLfileCache->remove(HTMLfileCache, file); | ||
676 | ret = NULL; | ||
677 | } | ||
678 | |||
679 | if (NULL == ret) | ||
680 | { | ||
681 | char *mm = MAP_FAILED; | ||
682 | |||
683 | ret = xmalloc(sizeof(HTMLfile)); | ||
684 | ret->fragments = qlist(QLIST_THREADSAFE); | ||
685 | length = sb.st_size; | ||
686 | ret->last.tv_sec = sb.st_mtim.tv_sec; | ||
687 | ret->last.tv_nsec = sb.st_mtim.tv_nsec; | ||
688 | |||
689 | I("Loading web template %s", file); | ||
690 | D("Web template %s is %d bytes long.", file, length); | ||
691 | |||
692 | mm = mmap(NULL, length, PROT_READ, MAP_SHARED | MAP_POPULATE, fd, 0); | ||
693 | if (mm == MAP_FAILED) | ||
694 | E("Failed to mmap %s", file); | ||
695 | else | ||
696 | { | ||
697 | char *h; | ||
698 | int i, j = 0, k = 0, l, m; | ||
699 | |||
700 | // Scan for server side includes style markings. | ||
701 | for (i = 0; i < length; i++) | ||
702 | { | ||
703 | if (i + 5 < length) | ||
704 | { | ||
705 | if (('<' == mm[i]) && ('!' == mm[i + 1]) && ('-' == mm[i + 2]) && ('-' == mm[i + 3]) && ('#' == mm[i + 4])) // '<!--#' | ||
706 | { | ||
707 | m = i; | ||
708 | i += 5; | ||
709 | if (i < length) | ||
710 | { | ||
711 | if (('e' == mm[i]) && ('c' == mm[i + 1]) && ('h' == mm[i + 2]) && ('o' == mm[i + 3]) && (' ' == mm[i + 4])) // 'echo ' | ||
712 | { | ||
713 | i += 5; | ||
714 | if (i + 5 < length) | ||
715 | { | ||
716 | if (('v' == mm[i]) && ('a' == mm[i + 1]) && ('r' == mm[i + 2]) && ('=' == mm[i + 3]) && ('"' == mm[i + 4])) // 'var="' | ||
717 | { | ||
718 | i += 5; | ||
719 | for (j = i; j < length; j++) | ||
720 | { | ||
721 | if ('"' == mm[j]) // '"' | ||
722 | { | ||
723 | frg1 = newFragment(FT_PARAM, &mm[i], j - i); | ||
724 | i = j + 1; | ||
725 | if (i + 4 < length) | ||
726 | { | ||
727 | if ((' ' == mm[i]) && ('-' == mm[i + 1]) && ('-' == mm[i + 2]) && ('>' == mm[i + 3])) // ' -->' | ||
728 | i += 4; | ||
729 | } | ||
730 | frg0 = newFragment(FT_TEXT, &mm[k], m - k); | ||
731 | ret->fragments->addlast(ret->fragments, frg0, sizeof(*frg0)); | ||
732 | ret->fragments->addlast(ret->fragments, frg1, sizeof(*frg1)); | ||
733 | k = i; | ||
734 | break; | ||
735 | } | ||
736 | } | ||
737 | } | ||
738 | } | ||
739 | } | ||
740 | } | ||
741 | } | ||
742 | } | ||
743 | } | ||
744 | frg0 = newFragment(FT_TEXT, &mm[k], length - k); | ||
745 | ret->fragments->addlast(ret->fragments, frg0, sizeof(*frg0)); | ||
746 | |||
747 | if (-1 == munmap(mm, length)) | ||
748 | FCGI_fprintf(FCGI_stderr, "Failed to munmap %s\n", file); | ||
749 | |||
750 | HTMLfileCache->put(HTMLfileCache, file, ret, sizeof(*ret)); | ||
751 | } | ||
752 | } | ||
753 | close(fd); | ||
754 | } | ||
755 | } | ||
756 | |||
757 | return ret; | ||
758 | } | ||
759 | |||
760 | |||
761 | int main(int argc, char *argv[], char **env) | ||
762 | { | ||
763 | // don't segfault if our environment is crazy | ||
764 | if (!*argv) return 127; | ||
765 | |||
766 | char *cmd = *argv; | ||
767 | char *tmp; | ||
768 | qhashtbl_t *configs = qhashtbl(0, 0); | ||
769 | lua_State *L = luaL_newstate(); | ||
770 | MYSQL *database = NULL, *dbconn = NULL; | ||
771 | gridStats *stats = NULL; | ||
772 | int status, result, i; | ||
773 | void *vd; | ||
774 | |||
775 | pwd = getcwd(0, 0); | ||
776 | |||
777 | if (isatty(1)) | ||
778 | { | ||
779 | I("Outputting to a terminal, not a web server."); | ||
780 | // Check if we are already running inside the proper tmux server. | ||
781 | char *eTMUX = getenv("TMUX"); | ||
782 | memset(toybuf, 0, sizeof(toybuf)); | ||
783 | snprintf(toybuf, sizeof(toybuf), "%s/%s", scRoot, Tsocket); | ||
784 | if (((eTMUX) && (0 == strncmp(toybuf, eTMUX, strlen(toybuf))))) | ||
785 | { | ||
786 | I("Running inside the proper tmux server."); | ||
787 | isTmux = 1; | ||
788 | } | ||
789 | else | ||
790 | I("Not running inside the proper tmux server, starting it."); | ||
791 | I("libfcgi version: %s", FCGI_VERSION); | ||
792 | I("Lua version: %s", LUA_RELEASE); | ||
793 | I("LuaJIT version: %s", LUAJIT_VERSION); | ||
794 | I("MariaDB / MySQL client version: %s", mysql_get_client_info()); | ||
795 | } | ||
796 | else | ||
797 | isWeb = 1; | ||
798 | |||
799 | |||
800 | /* From http://luajit.org/install.html - | ||
801 | To change or extend the list of standard libraries to load, copy | ||
802 | src/lib_init.c to your project and modify it accordingly. Make sure the | ||
803 | jit library is loaded or the JIT compiler will not be activated. | ||
804 | */ | ||
805 | luaL_openlibs(L); // Load Lua libraries. | ||
806 | |||
807 | // Load the config scripts. | ||
808 | char *cPaths[] = | ||
809 | { | ||
810 | "/etc/sledjChisl.conf.lua", | ||
811 | // "/etc/sledjChisl.d/*.lua", | ||
812 | "~/.sledjChisl.conf.lua", | ||
813 | // "~/.config/sledjChisl/*.lua", | ||
814 | ".sledjChisl.conf.lua", | ||
815 | NULL | ||
816 | }; | ||
817 | struct stat st; | ||
818 | |||
819 | |||
820 | for (i = 0; cPaths[i]; i++) | ||
821 | { | ||
822 | memset(toybuf, 0, sizeof(toybuf)); | ||
823 | if (('/' == cPaths[i][0]) || ('~' == cPaths[i][0])) | ||
824 | snprintf(toybuf, sizeof(toybuf), "%s", cPaths[i]); | ||
825 | else | ||
826 | snprintf(toybuf, sizeof(toybuf), "%s/%s", pwd, cPaths[i]); | ||
827 | if (0 != lstat(toybuf, &st)) | ||
828 | continue; | ||
829 | if (!isWeb) I("Loading configuration file - %s", toybuf); | ||
830 | status = luaL_loadfile(L, toybuf); | ||
831 | if (status) // If something went wrong, error message is at the top of the stack. | ||
832 | E("Couldn't load file: %s", lua_tostring(L, -1)); | ||
833 | else | ||
834 | { | ||
835 | result = lua_pcall(L, 0, LUA_MULTRET, 0); | ||
836 | if (result) | ||
837 | E("Failed to run script: %s", lua_tostring(L, -1)); | ||
838 | else | ||
839 | { | ||
840 | lua_getglobal(L, "config"); | ||
841 | lua_pushnil(L); | ||
842 | while(lua_next(L, -2) != 0) | ||
843 | { | ||
844 | char *n = (char *) lua_tostring(L, -2); | ||
845 | |||
846 | // Numbers can convert to strings, so check for numbers before checking for strings. | ||
847 | // On the other hand, strings that can be converted to numbers also pass lua_isnumber(). sigh | ||
848 | if (lua_isnumber(L, -1)) | ||
849 | { | ||
850 | float v = lua_tonumber(L, -1); | ||
851 | configs->put(configs, n, &v, sizeof(float)); | ||
852 | } | ||
853 | else if (lua_isstring(L, -1)) | ||
854 | configs->putstr(configs, n, (char *) lua_tostring(L, -1)); | ||
855 | else | ||
856 | { | ||
857 | char *v = (char *) lua_tostring(L, -1); | ||
858 | E("Unknown config variable type for %s = %s", n, v); | ||
859 | } | ||
860 | lua_pop(L, 1); | ||
861 | } | ||
862 | } | ||
863 | } | ||
864 | } | ||
865 | if ((vd = configs->get (configs, "loadAverageInc", NULL, false)) != NULL) {loadAverageInc = *((float *) vd); D("Setting loadAverageInc = %f", loadAverageInc);} | ||
866 | if ((vd = configs->get (configs, "simTimeOut", NULL, false)) != NULL) {simTimeOut = (int) *((float *) vd); D("Setting simTimeOut = %d", simTimeOut);} | ||
867 | if ((tmp = configs->getstr(configs, "scRoot", false)) != NULL) {scRoot = tmp; D("Setting scRoot = %s", scRoot);} | ||
868 | if ((tmp = configs->getstr(configs, "scUser", false)) != NULL) {scUser = tmp; D("Setting scUser = %s", scUser);} | ||
869 | if ((tmp = configs->getstr(configs, "Tconsole", false)) != NULL) {Tconsole = tmp; D("Setting Tconsole = %s", Tconsole);} | ||
870 | if ((tmp = configs->getstr(configs, "Tsocket", false)) != NULL) {Tsocket = tmp; D("Setting Tsocket = %s", Tsocket);} | ||
871 | if ((tmp = configs->getstr(configs, "Ttab", false)) != NULL) {Ttab = tmp; D("Setting Ttab = %s", Ttab);} | ||
872 | if ((tmp = configs->getstr(configs, "webRoot", false)) != NULL) {webRoot = tmp; D("Setting webRoot = %s", webRoot);} | ||
873 | |||
874 | |||
875 | if (isTmux || isWeb) | ||
876 | { | ||
877 | char *d; | ||
878 | memset(toybuf, 0, sizeof(toybuf)); | ||
879 | // TODO - the problem here is that web server isn't in the opensimmc group, so can't read this file ,with the database credentials. | ||
880 | // Other web server things have access to database credentials, so not like this is a big problem. | ||
881 | // For now I've just opened up the perms on it on my desktop. | ||
882 | snprintf(toybuf, sizeof(toybuf), "%s/config/config.ini", scRoot); | ||
883 | |||
884 | qlisttbl_t *qconfig = qconfig_parse_file(NULL, toybuf, '='); | ||
885 | d = qstrunchar(qconfig->getstr(qconfig, "Const.ConnectionString", false), '"', '"'); | ||
886 | |||
887 | if (NULL == d) | ||
888 | { | ||
889 | E("No database credentials in %s!", toybuf); | ||
890 | goto finished; | ||
891 | } | ||
892 | else | ||
893 | { | ||
894 | char *p0, *p1, *p2; | ||
895 | if (NULL == (d = strdup(d))) | ||
896 | { | ||
897 | E("Out of memory!"); | ||
898 | goto finished; | ||
899 | } | ||
900 | // Data Source=MYSQL_HOST;Database=MYSQL_DB;User ID=MYSQL_USER;Password=MYSQL_PASSWORD;Old Guids=true; | ||
901 | p0 = d; | ||
902 | while (NULL != p0) | ||
903 | { | ||
904 | p1 = strchr(p0, '='); | ||
905 | if (NULL == p1) break; | ||
906 | *p1 = '\0'; | ||
907 | p2 = strchr(p1 + 1, ';'); | ||
908 | if (NULL == p2) break; | ||
909 | *p2 = '\0'; | ||
910 | configs->putstr(configs, p0, p1 + 1); // NOTE - this allocs memory for it's key and it's data. | ||
911 | p0 = p2 + 1; | ||
912 | if ('\0' == *p0) | ||
913 | p0 = NULL; | ||
914 | }; | ||
915 | free(d); | ||
916 | } | ||
917 | if (mysql_library_init(argc, argv, NULL)) | ||
918 | { | ||
919 | E("mysql_library_init() failed!"); | ||
920 | goto finished; | ||
921 | } | ||
922 | |||
923 | database = mysql_init(NULL); | ||
924 | if (NULL == database) | ||
925 | { | ||
926 | E("mysql_init() failed - %s", mysql_error(database)); | ||
927 | goto finished; | ||
928 | } | ||
929 | else | ||
930 | { | ||
931 | // I have no idea what evil magic toybox is doing, but this ALWAYS CRASHES, no matter what I do. | ||
932 | dbconn = mysql_real_connect(database, | ||
933 | configs->getstr(configs, "Data Source", true), | ||
934 | configs->getstr(configs, "User ID", true), | ||
935 | configs->getstr(configs, "Password", true), | ||
936 | configs->getstr(configs, "Database", true), | ||
937 | // 3036, "/var/run/mysqld/mysqld.sock", | ||
938 | 0, NULL, | ||
939 | CLIENT_FOUND_ROWS | CLIENT_LOCAL_FILES | CLIENT_MULTI_STATEMENTS | CLIENT_MULTI_RESULTS); | ||
940 | if (NULL == dbconn) | ||
941 | { | ||
942 | E("mysql_real_connect() failed - %s", mysql_error(database)); | ||
943 | goto finished; | ||
944 | } | ||
945 | |||
946 | // Need to kick this off. | ||
947 | stats = getStats(database, stats); | ||
948 | char *h = qstrunchar(qconfig->getstr(qconfig, "Const.HostName", false), '"', '"'); | ||
949 | char *p = qstrunchar(qconfig->getstr(qconfig, "Const.PublicPort", false), '"', '"'); | ||
950 | stats->stats->putstr(stats->stats, "grid", qstrunchar(qconfig->getstr(qconfig, "Const.GridName", false), '"', '"')); | ||
951 | stats->stats->putstr(stats->stats, "HostName", h); | ||
952 | stats->stats->putstr(stats->stats, "PublicPort", p); | ||
953 | snprintf(toybuf, sizeof(toybuf), "http://%s:%s/", h, p); | ||
954 | |||
955 | stats->stats->putstr(stats->stats, "uri", toybuf); | ||
956 | } | ||
957 | } | ||
958 | |||
959 | |||
960 | if (isWeb) | ||
961 | { | ||
962 | char **initialEnv = env; | ||
963 | int count = 0, entries, bytes; | ||
964 | |||
965 | // FCGI_LISTENSOCK_FILENO is the socket to the web server. | ||
966 | // STDOUT and STDERR go to the web servers error log, or at least it does in Apache 2. | ||
967 | // fprintf(stdout, "STDOUT Started SledjChisl web server.\n"); | ||
968 | // fprintf(stderr, "STDERR Started SledjChisl web server.\n"); | ||
969 | I("Running SledjChisl inside a web server."); | ||
970 | |||
971 | if (1 == argc) | ||
972 | D("no args"); | ||
973 | else | ||
974 | { | ||
975 | for (i = 0; argv[i] != NULL; i++) | ||
976 | D("ARG %s", argv[i]); | ||
977 | } | ||
978 | printEnv(env); | ||
979 | |||
980 | struct stat statbuf; | ||
981 | if (-1 == fstat(FCGI_LISTENSOCK_FILENO, &statbuf)) | ||
982 | tb_error_msg("fstat() failed"); | ||
983 | else | ||
984 | { | ||
985 | if (S_ISREG (statbuf.st_mode)) D("regular file"); | ||
986 | else if (S_ISDIR (statbuf.st_mode)) D("directory"); | ||
987 | else if (S_ISCHR (statbuf.st_mode)) D("character device"); | ||
988 | else if (S_ISBLK (statbuf.st_mode)) D("block device"); | ||
989 | else if (S_ISFIFO(statbuf.st_mode)) D("FIFO (named pipe)"); | ||
990 | else if (S_ISLNK (statbuf.st_mode)) D("symbolic link"); | ||
991 | else if (S_ISSOCK(statbuf.st_mode)) D("socket"); | ||
992 | else D("unknown file descriptor type"); | ||
993 | } | ||
994 | |||
995 | |||
996 | while (FCGI_Accept() != -1) | ||
997 | { | ||
998 | if (NULL == getenv("PATH_INFO")) {msleep(1000); continue;} | ||
999 | |||
1000 | char *contentLength = getenv("CONTENT_LENGTH"); | ||
1001 | int len; | ||
1002 | |||
1003 | if (contentLength != NULL) | ||
1004 | len = strtol(contentLength, NULL, 10); | ||
1005 | else | ||
1006 | len = 0; | ||
1007 | |||
1008 | |||
1009 | if (FCGX_IsCGI()) | ||
1010 | D("Started SledjChisl CGI web request ROLE = %s!", getenv("FCGI_ROLE")); | ||
1011 | else | ||
1012 | D("Started SledjChisl FCGI web request ROLE = %s.", getenv("FCGI_ROLE")); | ||
1013 | |||
1014 | memset(toybuf, 0, sizeof(toybuf)); | ||
1015 | snprintf(toybuf, sizeof(toybuf), "%s/html%s", webRoot, getenv("PATH_INFO")); | ||
1016 | |||
1017 | HTMLfile *thisFile = checkHTMLcache(toybuf); | ||
1018 | |||
1019 | getStats(database, stats); | ||
1020 | |||
1021 | // This is dynamic content, it's always gonna be modified. I think. | ||
1022 | // char *since = getenv("If-Modified-Since"); | ||
1023 | // if (NULL != since) | ||
1024 | // { | ||
1025 | // time_t snc = qtime_parse_gmtstr(since); | ||
1026 | // if (thisFile->last.tv_sec < snc) | ||
1027 | // { | ||
1028 | // D("Status: 304 Not Modified - %s", toybuf); | ||
1029 | // FCGI_printf("Status: 304 Not Modified\r\n"); | ||
1030 | // goto fcgiDone; | ||
1031 | // } | ||
1032 | // } | ||
1033 | |||
1034 | FCGI_printf("Status: 200 OK\r\n" | ||
1035 | "Content-type: text/html\r\n" | ||
1036 | "\r\n"); | ||
1037 | if (len <= 0) | ||
1038 | D("No data from standard input."); | ||
1039 | else | ||
1040 | { | ||
1041 | int i, ch; | ||
1042 | |||
1043 | FCGI_printf("Standard input:<br>\n<pre>\n"); | ||
1044 | for (i = 0; i < len; i++) | ||
1045 | { | ||
1046 | if ((ch = FCGI_getchar()) < 0) | ||
1047 | { | ||
1048 | E("Error: Not enough bytes received on standard input<p>"); | ||
1049 | break; | ||
1050 | } | ||
1051 | FCGI_putchar(ch); | ||
1052 | } | ||
1053 | FCGI_printf("\n</pre><p>\n"); | ||
1054 | } | ||
1055 | |||
1056 | |||
1057 | qlist_obj_t obj; | ||
1058 | memset((void *) &obj, 0, sizeof(obj)); // must be cleared before call | ||
1059 | thisFile->fragments->lock(thisFile->fragments); | ||
1060 | while (thisFile->fragments->getnext(thisFile->fragments, &obj, false) == true) | ||
1061 | { | ||
1062 | fragment *frg = (fragment *) obj.data; | ||
1063 | if (NULL == frg->text) | ||
1064 | { | ||
1065 | E("NULL fragment!"); | ||
1066 | continue; | ||
1067 | } | ||
1068 | switch (frg->type) | ||
1069 | { | ||
1070 | case FT_TEXT: | ||
1071 | { | ||
1072 | // Silly thing triggers a "mod_fcgid: ap_pass_brigade failed in handle_request_ipc function" in Apache 2.4, sometimes. | ||
1073 | // Haven't seen one for some time. | ||
1074 | // https://www.tablix.org/~avian/blog/archives/2016/05/on_ap_pass_brigade_failed/ | ||
1075 | int l = FCGI_fwrite(frg->text, 1, frg->length, FCGI_stdout); | ||
1076 | if (l != frg->length) | ||
1077 | E("Failed to write %d != %d", l, frg->length); | ||
1078 | break; | ||
1079 | } | ||
1080 | |||
1081 | case FT_PARAM: | ||
1082 | { | ||
1083 | if (strcmp("DEBUG", frg->text) == 0) | ||
1084 | { | ||
1085 | FCGI_printf("<h1>FastCGI SledjChisl</h1>\n" | ||
1086 | "Request number %d, Process ID: %d<p>\n", ++count, getpid()); | ||
1087 | FCGI_printf("<p>libfcgi version: %s</p>\n", FCGI_VERSION); | ||
1088 | FCGI_printf("<p>Lua version: %s</p>\n", LUA_RELEASE); | ||
1089 | FCGI_printf("<p>LuaJIT version: %s</p>\n", LUAJIT_VERSION); | ||
1090 | FCGI_printf("<p>MySQL client version: %s</p>\n", mysql_get_client_info()); | ||
1091 | PrintEnv("Initial environment", initialEnv); | ||
1092 | PrintEnv("Request environment", environ); | ||
1093 | } | ||
1094 | else if (strcmp("URL", frg->text) == 0) | ||
1095 | FCGI_printf("%s://%s%s", getenv("REQUEST_SCHEME"), getenv("HTTP_HOST"), getenv("SCRIPT_NAME")); | ||
1096 | else | ||
1097 | { | ||
1098 | if ((tmp = stats->stats->getstr(stats->stats, frg->text, false)) != NULL) | ||
1099 | FCGI_printf("%s", tmp); | ||
1100 | else | ||
1101 | FCGI_printf("<b>%s</b>", frg->text); | ||
1102 | } | ||
1103 | break; | ||
1104 | } | ||
1105 | |||
1106 | case FT_LUA: | ||
1107 | break; | ||
1108 | } | ||
1109 | } | ||
1110 | thisFile->fragments->unlock(thisFile->fragments); | ||
1111 | |||
1112 | fcgiDone: | ||
1113 | D("Finishing SledjChisl web request."); | ||
1114 | FCGI_Finish(); | ||
1115 | } | ||
1116 | |||
1117 | FCGI_fprintf(FCGI_stdout, "Stopped SledjChisl web server.\n"); | ||
1118 | D("Stopped SledjChisl web server."); | ||
1119 | |||
1120 | goto finished; | ||
1121 | } | ||
1122 | |||
1123 | |||
1124 | if (!isTmux) | ||
1125 | { // Let's see if the proper tmux server is even running. | ||
1126 | memset(toybuf, 0, sizeof(toybuf)); | ||
1127 | snprintf(toybuf, sizeof(toybuf), "%s %s/%s -q list-sessions 2>/dev/null | grep -q %s:", Tcmd, scRoot, Tsocket, Tconsole); | ||
1128 | i = system(toybuf); | ||
1129 | if (WIFEXITED(i)) | ||
1130 | { | ||
1131 | if (0 != WEXITSTATUS(i)) // No such sesion, create it. | ||
1132 | { | ||
1133 | memset(toybuf, 0, sizeof(toybuf)); | ||
1134 | // The sudo is only so that the session is owned by opensim, otherwise it's owned by whoever ran this script, which is a likely security hole. | ||
1135 | // After the session is created, we rely on the caches directory to be group sticky, so that anyone in the opensim group can attach to the tmux socket. | ||
1136 | snprintf(toybuf, sizeof(toybuf), | ||
1137 | "sudo -Hu %s %s %s/%s new-session -d -s %s -n '%s' \\; split-window -bhp 50 -t '%s:' bash -c './sledjchisl; cd %s; bash'", | ||
1138 | scUser, Tcmd, scRoot, Tsocket, Tconsole, Ttab, Tconsole, scRoot); | ||
1139 | i = system(toybuf); | ||
1140 | if (!WIFEXITED(i)) | ||
1141 | E("tmux new-session command failed!"); | ||
1142 | } | ||
1143 | // Join the session. | ||
1144 | memset(toybuf, 0, sizeof(toybuf)); | ||
1145 | snprintf(toybuf, sizeof(toybuf), "%s %s/%s select-window -t '%s' \\; attach-session -t '%s'", Tcmd, scRoot, Tsocket, Tconsole, Tconsole); | ||
1146 | i = system(toybuf); | ||
1147 | if (!WIFEXITED(i)) | ||
1148 | E("tmux attach-session command failed!"); | ||
1149 | goto finished; | ||
1150 | } | ||
1151 | else | ||
1152 | E("tmux list-sessions command failed!"); | ||
1153 | } | ||
1154 | |||
1155 | |||
1156 | |||
1157 | simList *sims = getSims(); | ||
1158 | if (1) | ||
1159 | { | ||
1160 | struct sysinfo info; | ||
1161 | float la; | ||
1162 | |||
1163 | sysinfo(&info); | ||
1164 | la = info.loads[0]/65536.0; | ||
1165 | |||
1166 | if (!checkSimIsRunning("ROBUST")) | ||
1167 | { | ||
1168 | char *d = xmprintf("%s.{right}", Ttab); | ||
1169 | char *c = xmprintf("cd %s/current/bin", scRoot); | ||
1170 | |||
1171 | I("ROBUST is starting up."); | ||
1172 | sendTmuxCmd(d, c); | ||
1173 | free(c); | ||
1174 | c = xmprintf("mono Robust.exe -inidirectory=%s/config/ROBUST", scRoot); | ||
1175 | sendTmuxCmd(d, c); | ||
1176 | free(c); | ||
1177 | waitTmuxText(d, "INITIALIZATION COMPLETE FOR ROBUST"); | ||
1178 | I("ROBUST is done starting up."); | ||
1179 | la = waitLoadAverage(la, loadAverageInc / 3.0, simTimeOut / 3); | ||
1180 | free(d); | ||
1181 | } | ||
1182 | |||
1183 | for (i = 0; i < sims->num; i++) | ||
1184 | { | ||
1185 | char *sim = sims->sims[i], *name = getSimName(sims->sims[i]); | ||
1186 | |||
1187 | if (!checkSimIsRunning(sim)) | ||
1188 | { | ||
1189 | I("%s is starting up.", name); | ||
1190 | memset(toybuf, 0, sizeof(toybuf)); | ||
1191 | snprintf(toybuf, sizeof(toybuf), "%s %s/%s new-window -dn '%s' -t '%s:%d' 'cd %s/current/bin; mono OpenSim.exe -inidirectory=%s/config/%s'", | ||
1192 | Tcmd, scRoot, Tsocket, name, Tconsole, i + 1, scRoot, scRoot, sim); | ||
1193 | int r = system(toybuf); | ||
1194 | if (!WIFEXITED(r)) | ||
1195 | E("tmux new-window command failed!"); | ||
1196 | else | ||
1197 | { | ||
1198 | memset(toybuf, 0, sizeof(toybuf)); | ||
1199 | snprintf(toybuf, sizeof(toybuf), "INITIALIZATION COMPLETE FOR %s", name); | ||
1200 | waitTmuxText(name, toybuf); | ||
1201 | I("%s is done starting up.", name); | ||
1202 | la = waitLoadAverage(la, loadAverageInc, simTimeOut); | ||
1203 | } | ||
1204 | } | ||
1205 | } | ||
1206 | |||
1207 | } | ||
1208 | else if (!strcmp(cmd, "create")) // "create name x,y size" | ||
1209 | { | ||
1210 | } | ||
1211 | else if (!strcmp(cmd, "start")) // "start sim01" "start Welcome" "start" start everything | ||
1212 | { | ||
1213 | } | ||
1214 | else if (!strcmp(cmd, "backup")) // "backup onefang rejected" "backup sim01" "backup Welcome" "backup" backup everything | ||
1215 | { // If it's not a sim code, and not a sim name, it's an account inventory. | ||
1216 | } | ||
1217 | else if (!strcmp(cmd, "gitAR")) // "gitAR i name" | ||
1218 | { | ||
1219 | } | ||
1220 | else if (!strcmp(cmd, "stop")) // "stop sim01" "stop Welcome" "stop" stop everything | ||
1221 | { | ||
1222 | } | ||
1223 | |||
1224 | |||
1225 | double sum; | ||
1226 | |||
1227 | // Load the file containing the script we are going to run | ||
1228 | status = luaL_loadfile(L, "script.lua"); | ||
1229 | if (status) | ||
1230 | { | ||
1231 | // If something went wrong, error message is at the top of the stack | ||
1232 | E("Couldn't load file: %s", lua_tostring(L, -1)); | ||
1233 | goto finished; | ||
1234 | } | ||
1235 | |||
1236 | /* | ||
1237 | * Ok, now here we go: We pass data to the lua script on the stack. | ||
1238 | * That is, we first have to prepare Lua's virtual stack the way we | ||
1239 | * want the script to receive it, then ask Lua to run it. | ||
1240 | */ | ||
1241 | lua_newtable(L); /* We will pass a table */ | ||
1242 | |||
1243 | /* | ||
1244 | * To put values into the table, we first push the index, then the | ||
1245 | * value, and then call lua_rawset() with the index of the table in the | ||
1246 | * stack. Let's see why it's -3: In Lua, the value -1 always refers to | ||
1247 | * the top of the stack. When you create the table with lua_newtable(), | ||
1248 | * the table gets pushed into the top of the stack. When you push the | ||
1249 | * index and then the cell value, the stack looks like: | ||
1250 | * | ||
1251 | * <- [stack bottom] -- table, index, value [top] | ||
1252 | * | ||
1253 | * So the -1 will refer to the cell value, thus -3 is used to refer to | ||
1254 | * the table itself. Note that lua_rawset() pops the two last elements | ||
1255 | * of the stack, so that after it has been called, the table is at the | ||
1256 | * top of the stack. | ||
1257 | */ | ||
1258 | for (i = 1; i <= 5; i++) | ||
1259 | { | ||
1260 | lua_pushnumber(L, i); // Push the table index | ||
1261 | lua_pushnumber(L, i*2); // Push the cell value | ||
1262 | lua_rawset(L, -3); // Stores the pair in the table | ||
1263 | } | ||
1264 | |||
1265 | // By what name is the script going to reference our table? | ||
1266 | lua_setglobal(L, "foo"); | ||
1267 | |||
1268 | // Ask Lua to run our little script | ||
1269 | result = lua_pcall(L, 0, LUA_MULTRET, 0); | ||
1270 | if (result) | ||
1271 | { | ||
1272 | E("Failed to run script: %s", lua_tostring(L, -1)); | ||
1273 | goto finished; | ||
1274 | } | ||
1275 | |||
1276 | // Get the returned value at the top of the stack (index -1) | ||
1277 | sum = lua_tonumber(L, -1); | ||
1278 | |||
1279 | I("Script returned: %.0f", sum); | ||
1280 | |||
1281 | lua_pop(L, 1); // Take the returned value out of the stack | ||
1282 | |||
1283 | puts(""); | ||
1284 | fflush(stdout); | ||
1285 | |||
1286 | finished: | ||
1287 | if (database) mysql_close(database); | ||
1288 | mysql_library_end(); | ||
1289 | lua_close(L); | ||
1290 | if (stats) | ||
1291 | { | ||
1292 | if (stats->stats) stats->stats->free(stats->stats); | ||
1293 | free(stats); | ||
1294 | } | ||
1295 | if (configs) configs->free(configs); | ||
1296 | return EXIT_SUCCESS; | ||
1297 | } | ||