diff options
author | onefang | 2021-08-26 06:21:19 +1000 |
---|---|---|
committer | onefang | 2021-08-26 06:21:19 +1000 |
commit | cdfbb899f1112dab44d5490838765e9bd73bc60e (patch) | |
tree | 52cddd0b76e7ad8544a0ada533f91bb5fc402025 /src/sledjchisl | |
parent | Still failing to reconnect for dbCount(), just set the fucking timeout to a y... (diff) | |
parent | Don't strip (OWNER) out of script error report. (diff) | |
download | opensim-SC-cdfbb899f1112dab44d5490838765e9bd73bc60e.zip opensim-SC-cdfbb899f1112dab44d5490838765e9bd73bc60e.tar.gz opensim-SC-cdfbb899f1112dab44d5490838765e9bd73bc60e.tar.bz2 opensim-SC-cdfbb899f1112dab44d5490838765e9bd73bc60e.tar.xz |
Merge branch 'switch' into Domme.
Diffstat (limited to '')
-rw-r--r-- | src/sledjchisl/sledjchisl.c | 2971 |
1 files changed, 2407 insertions, 564 deletions
diff --git a/src/sledjchisl/sledjchisl.c b/src/sledjchisl/sledjchisl.c index 51f14eb..2691749 100644 --- a/src/sledjchisl/sledjchisl.c +++ b/src/sledjchisl/sledjchisl.c | |||
@@ -3,18 +3,86 @@ | |||
3 | * Copyright 2020 David Seikel <sledjchisl@sledjhamr.org> | 3 | * Copyright 2020 David Seikel <sledjchisl@sledjhamr.org> |
4 | * Not in SUSv4. An entirely new invention, thus no web site either. | 4 | * Not in SUSv4. An entirely new invention, thus no web site either. |
5 | 5 | ||
6 | USE_SLEDJCHISL(NEWTOY(sledjchisl, "m(mode):", TOYFLAG_USR|TOYFLAG_BIN)) | 6 | USE_SLEDJCHISL(NEWTOY(sledjchisl, "?vqma", TOYFLAG_USR|TOYFLAG_BIN)) |
7 | 7 | ||
8 | config SLEDJCHISL | 8 | config SLEDJCHISL |
9 | bool "sledjchisl" | 9 | bool "sledjchisl" |
10 | default y | 10 | default y |
11 | help | 11 | help |
12 | usage: sledjchisl [-m|--mode mode] | 12 | usage: sledjchisl [-vqma] [mode [name]] |
13 | 13 | ||
14 | opensim-SC management system. | 14 | opensim-SC management system. |
15 | |||
16 | -v verbose mode. | ||
17 | |||
18 | Mode selects what sledjchisl will do - | ||
19 | start | ||
20 | start -q | ||
21 | start Welcome | ||
22 | start Welcome.ini | ||
23 | start Welcome.shini | ||
24 | start "Welcome sim" | ||
25 | Will start a sim or everything. | ||
26 | -q when starting everything wont show the tmux console. | ||
27 | backup | ||
28 | backup -m | ||
29 | backup -m "Joan Smith" | ||
30 | backup Welcome | ||
31 | backup Welcome.ini | ||
32 | backup Welcome.shini | ||
33 | backup "Welcome sim" | ||
34 | Will backup sim or everything. | ||
35 | -m for a members inventory. | ||
36 | gitar m "Joan Smith" | ||
37 | gitar Welcome | ||
38 | gitar Welcome.ini | ||
39 | gitar Welcome.shini | ||
40 | gitar o "Welcome sim" | ||
41 | restart | ||
42 | restart Welcome | ||
43 | restart Welcome.ini | ||
44 | restart Welcome.shini | ||
45 | restart "Welcome sim" | ||
46 | Will stop then start a sim, or everything. | ||
47 | status | ||
48 | status Welcome | ||
49 | status Welcome.ini | ||
50 | status Welcome.shini | ||
51 | status "Welcome sim" | ||
52 | Will show status of a sim, or everything. | ||
53 | stop | ||
54 | stop -a | ||
55 | stop Welcome | ||
56 | stop Welcome.ini | ||
57 | stop Welcome.shini | ||
58 | stop "Welcome sim" | ||
59 | Will stop a sim, or everything. | ||
60 | -a when stopping everything will close down all tmux windows and panes. | ||
15 | */ | 61 | */ |
16 | 62 | ||
17 | 63 | ||
64 | // TODO - remember to rebuild all of toybox if any of the above changes. | ||
65 | // Toybox's strend overrides another strend that causes MariaDB library to crash. Renaming it to tb_strend helps. | ||
66 | // I deal with that by using a sed invokation when building toybox. | ||
67 | #define FOR_sledjchisl | ||
68 | #include "toys.h" | ||
69 | |||
70 | GLOBALS( | ||
71 | ) | ||
72 | |||
73 | /* | ||
74 | configs/sim01 - | ||
75 | backup-sim | ||
76 | start-sim | ||
77 | stop-sim | ||
78 | |||
79 | Currently they are all links to ../../current/scripts/start-sim, | ||
80 | they should be links to ../../current/bin/sledjchisl, | ||
81 | which itself is a link to ../src/build/toybox/generated/unstripped/toybox or ../src/build/toybox/toybox | ||
82 | They are obsolete now, should remove them. | ||
83 | |||
84 | */ | ||
85 | |||
18 | // TODO - figure out how to automate testing of this. | 86 | // TODO - figure out how to automate testing of this. |
19 | // Being all interactive and involving external web servers / viewers makes it hard. | 87 | // Being all interactive and involving external web servers / viewers makes it hard. |
20 | 88 | ||
@@ -45,6 +113,15 @@ extern char **environ; | |||
45 | #include <lauxlib.h> | 113 | #include <lauxlib.h> |
46 | #include <luajit.h> | 114 | #include <luajit.h> |
47 | 115 | ||
116 | // lua.h has LUA_T* NONE, NIL, BOOLEAN, LIGHTUSERDATA, NUMBER, STRING, TABLE, FUNCTION, USERDATA, THREAD as defines, -1 - 8. | ||
117 | // These are the missing ones. Then later we will have prim, mesh, script, sound, terrain, ... | ||
118 | #define LUA_TGROUP 42 | ||
119 | #define LUA_TINTEGER 43 | ||
120 | #define LUA_TEMAIL 44 | ||
121 | #define LUA_TPASSWORD 45 | ||
122 | #define LUA_TFILE 46 | ||
123 | #define LUA_TIMAGE 47 | ||
124 | |||
48 | #include "lib/fcgi_SC.h" | 125 | #include "lib/fcgi_SC.h" |
49 | #include "lib/handlekeys.h" | 126 | #include "lib/handlekeys.h" |
50 | 127 | ||
@@ -54,33 +131,45 @@ extern char **environ; | |||
54 | // https://mariadb.com/kb/en/about-mariadb-connector-c/ Official docs. | 131 | // https://mariadb.com/kb/en/about-mariadb-connector-c/ Official docs. |
55 | // http://dev.mysql.com/doc/refman/5.5/en/c-api-function-overview.html MySQL docs. | 132 | // http://dev.mysql.com/doc/refman/5.5/en/c-api-function-overview.html MySQL docs. |
56 | // http://zetcode.com/db/mysqlc/ MySQL tutorial. | 133 | // http://zetcode.com/db/mysqlc/ MySQL tutorial. |
57 | #include <my_global.h> | 134 | //#include <my_global.h> |
58 | #include <mysql.h> | 135 | #include <mysql.h> |
59 | 136 | ||
60 | #include <qlibc.h> | 137 | #include <qlibc.h> |
61 | #include <extensions/qconfig.h> | 138 | #include <extensions/qconfig.h> |
62 | 139 | ||
63 | // TODO - I should probably replace openSSL with something else. Only using it for the hash functions, and apparently it's got a bit of a bad rep. | 140 | // TODO - I should probably replace openSSL with something else. Only using it for the hash functions, and apparently it's got a bit of a bad rep. |
141 | // only sha 512 I think. Which qLibc doesn't have. It has MD5 128 bit, Mummur3 32 and 128 bit, and FNV1 32 and 64 bit. | ||
64 | // qLibc optionally uses openSSL for it's HTTP client stuff. | 142 | // qLibc optionally uses openSSL for it's HTTP client stuff. |
65 | #include <openssl/crypto.h> | 143 | #include <openssl/crypto.h> |
66 | #include <openssl/evp.h> | 144 | #include <openssl/evp.h> |
67 | #include "openssl/hmac.h" | 145 | #include "openssl/hmac.h" |
68 | #include <uuid/uuid.h> | 146 | #include <uuid/uuid.h> |
69 | 147 | ||
70 | // Toybox's strend overrides another strend that causes MariaDB library to crash. Renaming it to tb_strend helps. | 148 | typedef enum |
71 | // I deal with that by using a sed invokation when building toybox. | 149 | { |
72 | #include "toys.h" | 150 | START = 0, |
73 | 151 | BACKUP = 1, | |
74 | 152 | GITAR = 2, | |
75 | GLOBALS( | 153 | RESTART = 3, |
76 | char *mode; | 154 | STATUS = 4, |
77 | ) | 155 | STOP = 9 |
78 | 156 | } modes; | |
79 | #define TT this.sledjchisl | 157 | |
80 | 158 | modes currentMode = START; | |
81 | #define FLAG_m 2 | 159 | |
82 | 160 | char *modeStrings[] = | |
83 | 161 | { | |
162 | "start", | ||
163 | "backup", | ||
164 | "gitAR", | ||
165 | "restart", | ||
166 | "status", | ||
167 | "Huh?", | ||
168 | "WTF?", | ||
169 | "mumblefuck", | ||
170 | "dontDoThis" | ||
171 | "stop" | ||
172 | }; | ||
84 | 173 | ||
85 | // Duplicate some small amount of code from qLibc, coz /, + and, = are not good choices, and the standard says we can pick those. | 174 | // Duplicate some small amount of code from qLibc, coz /, + and, = are not good choices, and the standard says we can pick those. |
86 | /** | 175 | /** |
@@ -282,10 +371,10 @@ typedef enum | |||
282 | // Silly "man getrandom" is bullshitting. | 371 | // Silly "man getrandom" is bullshitting. |
283 | // Note - this is Linux specific, it's calling a Linux kernel function. | 372 | // Note - this is Linux specific, it's calling a Linux kernel function. |
284 | // Remove this when we have a real getrandom(), and replace it with - | 373 | // Remove this when we have a real getrandom(), and replace it with - |
285 | // #include <sys/random.h> | 374 | //#include <sys/random.h> |
286 | #include <sys/syscall.h> | 375 | #include <sys/syscall.h> |
287 | #include <linux/random.h> | 376 | #include <linux/random.h> |
288 | int getrandom(void *b, size_t l, unsigned int f) | 377 | ssize_t getrandom(void *b, size_t l, unsigned int f) |
289 | { | 378 | { |
290 | return (int) syscall(SYS_getrandom, b, l, f); | 379 | return (int) syscall(SYS_getrandom, b, l, f); |
291 | } | 380 | } |
@@ -427,10 +516,14 @@ char *scBackup = ""; | |||
427 | char *scCache = ""; | 516 | char *scCache = ""; |
428 | char *scData = ""; | 517 | char *scData = ""; |
429 | char *scLog = ""; | 518 | char *scLog = ""; |
519 | char *scTemp = ""; | ||
430 | char *Tconsole = "SledjChisl"; | 520 | char *Tconsole = "SledjChisl"; |
431 | char *Tsocket = "opensim-tmux.socket"; | 521 | char *Tsocket = "opensim-tmux.socket"; |
432 | char *Ttab = "SC"; | 522 | char *Ttab = "SC"; |
433 | char *Tcmd = "tmux -S"; | 523 | char *Tcmd = "tmux -S"; |
524 | char *backupIARsim = "Sandbox"; | ||
525 | char *rSync = ""; | ||
526 | int rSyncPort = 0; | ||
434 | char *webRoot = "/var/www/html"; | 527 | char *webRoot = "/var/www/html"; |
435 | char *URL = "fcgi-bin/sledjchisl.fcgi"; | 528 | char *URL = "fcgi-bin/sledjchisl.fcgi"; |
436 | char *ToS = "Be good."; | 529 | char *ToS = "Be good."; |
@@ -439,9 +532,11 @@ int seshRenew = 10 * 60; | |||
439 | int idleTimeOut = 30 * 60; | 532 | int idleTimeOut = 30 * 60; |
440 | int seshTimeOut = 24 * 60 * 60; | 533 | int seshTimeOut = 24 * 60 * 60; |
441 | int newbieTimeOut = 30; | 534 | int newbieTimeOut = 30; |
442 | float loadAverageInc = 0.5; | 535 | float loadAverageInc = 0.7; |
443 | int simTimeOut = 45; | 536 | int simTimeOut = 45; |
444 | boolean DEBUG = TRUE; | 537 | int bulkSims = 0; |
538 | boolean DEBUG = FALSE; | ||
539 | boolean VERBOSE = FALSE; | ||
445 | qhashtbl_t *mimeTypes; | 540 | qhashtbl_t *mimeTypes; |
446 | qlist_t *dbRequests; | 541 | qlist_t *dbRequests; |
447 | 542 | ||
@@ -462,8 +557,9 @@ char *logTypes[] = | |||
462 | "93", "WARNING", // yellow | 557 | "93", "WARNING", // yellow |
463 | "36", "TIMEOUT", // cyan | 558 | "36", "TIMEOUT", // cyan |
464 | "97;40", "INFO", // white | 559 | "97;40", "INFO", // white |
560 | "92;40", "VERBOSE", // green | ||
465 | "90", "DEBUG", // grey | 561 | "90", "DEBUG", // grey |
466 | // VERBOSE? UNKNOWN? FATAL? SILENT? All from Android aparently. | 562 | // UNKNOWN? FATAL? SILENT? All from Android apparently. |
467 | "35", "debug", // magenta | 563 | "35", "debug", // magenta |
468 | "34", "timeout", // blue | 564 | "34", "timeout", // blue |
469 | }; | 565 | }; |
@@ -477,6 +573,12 @@ void logMe(int v, char *format, ...) | |||
477 | struct timeval tv; | 573 | struct timeval tv; |
478 | time_t curtime; | 574 | time_t curtime; |
479 | char date[DATE_TIME_LEN]; | 575 | char date[DATE_TIME_LEN]; |
576 | char *t; | ||
577 | |||
578 | if ((!VERBOSE) && (4 < v)) | ||
579 | return; | ||
580 | if ((!DEBUG) && (5 < v)) | ||
581 | return; | ||
480 | 582 | ||
481 | va_start(va, format); | 583 | va_start(va, format); |
482 | va_copy(va2, va); | 584 | va_copy(va2, va); |
@@ -492,9 +594,10 @@ void logMe(int v, char *format, ...) | |||
492 | gettimeofday(&tv, NULL); | 594 | gettimeofday(&tv, NULL); |
493 | curtime = tv.tv_sec; | 595 | curtime = tv.tv_sec; |
494 | strftime(date, DATE_TIME_LEN, "(%Z %z) %F %T", localtime(&curtime)); | 596 | strftime(date, DATE_TIME_LEN, "(%Z %z) %F %T", localtime(&curtime)); |
495 | 597 | t = xmprintf("%.6d", (int) tv.tv_usec); | |
496 | v *= 2; | 598 | v *= 2; |
497 | fprintf(stderr, "%s.%.6ld \e[%sm%-8s sledjchisl: %s\e[0m\n", date, tv.tv_usec, logTypes[v], logTypes[v + 1], ret); | 599 | fprintf(stderr, "%s.%.2s \e[%sm%-8s %s\e[0m\n", date, t, logTypes[v], logTypes[v + 1], ret); |
600 | free(t); | ||
498 | free(ret); | 601 | free(ret); |
499 | } | 602 | } |
500 | #define C(...) logMe(0, __VA_ARGS__) | 603 | #define C(...) logMe(0, __VA_ARGS__) |
@@ -502,9 +605,115 @@ void logMe(int v, char *format, ...) | |||
502 | #define W(...) logMe(2, __VA_ARGS__) | 605 | #define W(...) logMe(2, __VA_ARGS__) |
503 | #define T(...) logMe(3, __VA_ARGS__) | 606 | #define T(...) logMe(3, __VA_ARGS__) |
504 | #define I(...) logMe(4, __VA_ARGS__) | 607 | #define I(...) logMe(4, __VA_ARGS__) |
505 | #define D(...) logMe(5, __VA_ARGS__) | 608 | #define V(...) logMe(5, __VA_ARGS__) |
506 | #define d(...) logMe(6, __VA_ARGS__) | 609 | #define D(...) logMe(6, __VA_ARGS__) |
507 | #define t(...) logMe(7, __VA_ARGS__) | 610 | #define d(...) logMe(7, __VA_ARGS__) |
611 | #define t(...) logMe(8, __VA_ARGS__) | ||
612 | |||
613 | |||
614 | int shellMeFailVf(char *format, ...) | ||
615 | { | ||
616 | va_list va, va2; | ||
617 | int len; | ||
618 | char *ret; | ||
619 | int result = FALSE; | ||
620 | |||
621 | va_start(va, format); | ||
622 | va_copy(va2, va); | ||
623 | // How long is it? | ||
624 | len = vsnprintf(0, 0, format, va); | ||
625 | len++; | ||
626 | va_end(va); | ||
627 | // Allocate and do the sprintf() | ||
628 | ret = xmalloc(len); | ||
629 | vsnprintf(ret, len, format, va2); | ||
630 | va_end(va2); | ||
631 | |||
632 | V(ret); | ||
633 | free(ret); | ||
634 | return result; | ||
635 | } | ||
636 | int shellMeFailVt(char *format, ...) | ||
637 | { | ||
638 | va_list va, va2; | ||
639 | int len; | ||
640 | char *ret; | ||
641 | int result = TRUE; | ||
642 | |||
643 | va_start(va, format); | ||
644 | va_copy(va2, va); | ||
645 | // How long is it? | ||
646 | len = vsnprintf(0, 0, format, va); | ||
647 | len++; | ||
648 | va_end(va); | ||
649 | // Allocate and do the sprintf() | ||
650 | ret = xmalloc(len); | ||
651 | vsnprintf(ret, len, format, va2); | ||
652 | va_end(va2); | ||
653 | |||
654 | V(ret); | ||
655 | free(ret); | ||
656 | return result; | ||
657 | } | ||
658 | |||
659 | int shellMeFail(char *format, ...) | ||
660 | { | ||
661 | va_list va, va2; | ||
662 | int len; | ||
663 | char *ret; | ||
664 | int result; | ||
665 | |||
666 | va_start(va, format); | ||
667 | va_copy(va2, va); | ||
668 | // How long is it? | ||
669 | len = vsnprintf(0, 0, format, va); | ||
670 | len++; | ||
671 | va_end(va); | ||
672 | // Allocate and do the sprintf() | ||
673 | ret = xmalloc(len); | ||
674 | vsnprintf(ret, len, format, va2); | ||
675 | va_end(va2); | ||
676 | |||
677 | result = !WIFEXITED(system(ret)); | ||
678 | free(ret); | ||
679 | return result; | ||
680 | } | ||
681 | |||
682 | int shellMe(char *format, ...) | ||
683 | { | ||
684 | va_list va, va2; | ||
685 | int len; | ||
686 | char *ret; | ||
687 | int result; | ||
688 | |||
689 | va_start(va, format); | ||
690 | va_copy(va2, va); | ||
691 | // How long is it? | ||
692 | len = vsnprintf(0, 0, format, va); | ||
693 | len++; | ||
694 | va_end(va); | ||
695 | // Allocate and do the sprintf() | ||
696 | ret = xmalloc(len); | ||
697 | vsnprintf(ret, len, format, va2); | ||
698 | va_end(va2); | ||
699 | |||
700 | result = system(ret); | ||
701 | free(ret); | ||
702 | return result; | ||
703 | } | ||
704 | |||
705 | |||
706 | int hasContents(char *file) | ||
707 | { | ||
708 | struct stat st; | ||
709 | |||
710 | if (0 == lstat(file, &st)) | ||
711 | { | ||
712 | if (0 != st.st_size) | ||
713 | return TRUE; | ||
714 | } | ||
715 | return FALSE; | ||
716 | } | ||
508 | 717 | ||
509 | 718 | ||
510 | static void addStrL(qlist_t *list, char *s) | 719 | static void addStrL(qlist_t *list, char *s) |
@@ -523,6 +732,28 @@ static char *getStrH(qhashtbl_t *hash, char *key) | |||
523 | } | 732 | } |
524 | 733 | ||
525 | 734 | ||
735 | static void bitch(reqData *Rd, char *message, char *log) | ||
736 | { | ||
737 | addStrL(Rd->errors, message); | ||
738 | E("%s %s %s - %s %s", getStrH(Rd->headers, "REMOTE_ADDR"), Rd->shs.UUID, Rd->shs.name, message, log); | ||
739 | } | ||
740 | |||
741 | /* "A token cookie that references a non-existent session, its value should be replaced immediately to prevent session fixation." | ||
742 | https://owasp.org/www-community/attacks/Session_fixation | ||
743 | Which describes the problem, but offers no solution. | ||
744 | See https://stackoverflow.com/questions/549/the-definitive-guide-to-form-based-website-authentication?rq=1. | ||
745 | I think this means send a new cookie. | ||
746 | I clear out the cookies and send blank ones with -1 maxAge, so they should get deleted. | ||
747 | */ | ||
748 | static void bitchSession(reqData *Rd, char *message, char *log) | ||
749 | { | ||
750 | if ('\0' != message[0]) | ||
751 | addStrL(Rd->errors, message); | ||
752 | C("%s %s %s - %s %s", getStrH(Rd->headers, "REMOTE_ADDR"), Rd->shs.UUID, Rd->shs.name, message, log); | ||
753 | Rd->shs.status = SHS_BOGUS; | ||
754 | } | ||
755 | |||
756 | |||
526 | char *myHMAC(char *in, boolean b64) | 757 | char *myHMAC(char *in, boolean b64) |
527 | { | 758 | { |
528 | EVP_MD_CTX *mdctx = EVP_MD_CTX_create(); // Gets renamed to EVP_MD_CTX_new() in later versions. | 759 | EVP_MD_CTX *mdctx = EVP_MD_CTX_create(); // Gets renamed to EVP_MD_CTX_new() in later versions. |
@@ -571,39 +802,371 @@ void PrintTable(lua_State *L) | |||
571 | } | 802 | } |
572 | } | 803 | } |
573 | 804 | ||
805 | typedef struct _qLua qLua; | ||
806 | struct _qLua | ||
807 | { | ||
808 | int type; | ||
809 | union | ||
810 | { | ||
811 | boolean b; | ||
812 | int i; | ||
813 | float f; | ||
814 | char *s; | ||
815 | qtreetbl_t *t; | ||
816 | } v; | ||
817 | }; | ||
574 | 818 | ||
575 | int sendTmuxKeys(char *dest, char *keys) | 819 | qLua *qLuaGet(qtreetbl_t *tree, char *key) |
576 | { | 820 | { |
577 | int ret = 0, i; | 821 | return (qLua *) tree->get(tree, key, NULL, false); |
578 | char *c = xmprintf("%s %s/%s send-keys -t %s:%s '%s'", Tcmd, scRun, Tsocket, Tconsole, dest, keys); | 822 | } |
823 | |||
824 | void dumpLuaTree(qtreetbl_t *tree, char *name, int depth) | ||
825 | { | ||
826 | qtreetbl_obj_t obj0; | ||
827 | |||
828 | if (0 == depth) | ||
829 | fprintf(stderr, "DUMP Lua table %s -\n%s =\n{\n", name, name); | ||
830 | else | ||
831 | fprintf(stderr, "%*s[%s] =\n%*s{\n", depth * 2, "", name, depth * 2, ""); | ||
832 | memset((void*)&obj0, 0, sizeof(obj0)); // must be cleared before call | ||
833 | tree->lock(tree); // lock it when thread condition is expected | ||
834 | while(tree->getnext(tree, &obj0, false) == true) | ||
835 | { | ||
836 | qLua *q0 = obj0.data; | ||
837 | |||
838 | switch (q0->type) | ||
839 | { | ||
840 | case LUA_TBOOLEAN : {fprintf(stderr, "%*s[%s] = %d,\n", (depth + 1) * 2, "", obj0.name, q0->v.b); break;} | ||
841 | case LUA_TINTEGER : {fprintf(stderr, "%*s[%s] = %d,\n", (depth + 1) * 2, "", obj0.name, q0->v.i); break;} | ||
842 | case LUA_TNUMBER : {fprintf(stderr, "%*s[%s] = %f,\n", (depth + 1) * 2, "", obj0.name, q0->v.f); break;} | ||
843 | case LUA_TSTRING : {fprintf(stderr, "%*s[%s] = %s,\n", (depth + 1) * 2, "", obj0.name, q0->v.s); break;} | ||
844 | case LUA_TTABLE : {dumpLuaTree(q0->v.t, obj0.name, depth + 1); break;} | ||
845 | default : {fprintf(stderr, "%*s[%s] ? ??,\n", (depth + 1) * 2, "", obj0.name); break;} | ||
846 | } | ||
847 | } | ||
848 | tree->unlock(tree); | ||
849 | fprintf(stderr, "%*s},\n", depth * 2, ""); | ||
850 | } | ||
851 | |||
852 | qtreetbl_t *lua2tree() | ||
853 | { | ||
854 | qtreetbl_t *ret = qtreetbl(0); | ||
855 | |||
856 | if (NULL != ret) | ||
857 | { | ||
858 | qLua q; | ||
859 | |||
860 | lua_pushnil(L); // +1 nil, first key | ||
861 | while(lua_next(L, -2) != 0) // -1 key, +2 next key, value (or 0 if nothing left in table) | ||
862 | { | ||
863 | // stack now contains: -1 => value; -2 => key; -3 => table | ||
864 | // copy the key so that lua_tostring does not modify the original | ||
865 | lua_pushvalue(L, -2); | ||
866 | // stack now contains: -1 => key; -2 => value; -3 => key; -4 => table | ||
867 | char *n = (char *) lua_tostring(L, -1); // 0, modifies key copy | ||
868 | int t = lua_type(L, -3); | ||
869 | |||
870 | // Correct the sort order. Assuming we will never have more than 999,999 sims. Think that's a safe assumption. | ||
871 | /* https://www.hypergridbusiness.com/2021/07/opensim-land-growth/ | ||
872 | "Total area increased by the equivalent of 2,252 standard regions, for a new record high of 103,625 regions. | ||
873 | That’s nearly four times the area of Second Life," | ||
874 | */ | ||
875 | if ((LUA_TINTEGER == t) || (LUA_TNUMBER == t)) | ||
876 | { | ||
877 | char *tmp = xmprintf("%6s", n); | ||
878 | n = tmp; | ||
879 | } | ||
880 | q.type = lua_type(L, -2); // 0 | ||
881 | switch(q.type) | ||
882 | { | ||
883 | case LUA_TBOOLEAN : {q.v.b = lua_toboolean(L, -2); break;} | ||
884 | case LUA_TINTEGER : {q.v.i = lua_tointeger(L, -2); break;} | ||
885 | case LUA_TNUMBER : {q.v.f = lua_tonumber(L, -2); break;} | ||
886 | case LUA_TSTRING : {q.v.s = (char *) lua_tostring(L, -2); break;} | ||
887 | case LUA_TTABLE : {lua_pushvalue(L, -2); q.v.t = lua2tree(); lua_pop(L, 1); break;} | ||
888 | |||
889 | default : | ||
890 | { | ||
891 | q.v.s = (char *) lua_tostring(L, -2); // 0 | ||
892 | E("Unknown Lua variable type for %s = %s is %d", n, q.v.s, q.type); | ||
893 | break; | ||
894 | } | ||
895 | } | ||
896 | ret->put(ret, n, &q, sizeof(qLua)); | ||
897 | if ((LUA_TINTEGER == t) || (LUA_TNUMBER == t)) | ||
898 | free(n); | ||
899 | // pop value + copy of key, leaving original key | ||
900 | lua_pop(L, 2); // -2 value and copy of key | ||
901 | // stack now contains: -1 => key; -2 => table | ||
902 | } | ||
903 | } | ||
904 | else | ||
905 | { | ||
906 | D("No memory left."); | ||
907 | perror_msg("Unable to allocate memory"); | ||
908 | } | ||
579 | 909 | ||
580 | i = system(c); | ||
581 | if (!WIFEXITED(i)) | ||
582 | E("tmux send-keys command failed!"); | ||
583 | free(c); | ||
584 | return ret; | 910 | return ret; |
585 | } | 911 | } |
586 | 912 | ||
587 | int sendTmuxCmd(char *dest, char *cmd) | 913 | qtreetbl_t *Lua2tree(char *file, char *var) |
588 | { | 914 | { |
589 | int ret = 0, i; | 915 | qtreetbl_t *ret = NULL; |
590 | char *c = xmprintf("%s %s/%s send-keys -t %s:'%s' '%s' Enter", Tcmd, scRun, Tsocket, Tconsole, dest, cmd); | 916 | struct stat st; |
591 | 917 | ||
592 | i = system(c); | 918 | if (0 != lstat(file, &st)) |
593 | if (!WIFEXITED(i)) | 919 | { |
594 | E("tmux send-keys command failed!"); | 920 | D("No %s file.", file); |
595 | free(c); | 921 | perror_msg("Unable to stat %s", file); |
922 | } | ||
923 | else | ||
924 | { | ||
925 | if (luaL_loadfile(L, file)) // +1 the chunk or an error message. If something went wrong, error message is at the top of the stack. | ||
926 | E("Couldn't load Lua file: %s", lua_tostring(L, -1)); // 0 | ||
927 | else | ||
928 | { | ||
929 | if (lua_pcall(L, 0, LUA_MULTRET, 0)) // +1 result or an error message. Except we get 0 or error? | ||
930 | { | ||
931 | E("Failed to run Lua script: %s", lua_tostring(L, -1)); // 0 | ||
932 | lua_pop(L, 1); // -1 chunk or error message | ||
933 | } | ||
934 | else | ||
935 | { | ||
936 | lua_getglobal(L, var); // +1 the value of var | ||
937 | ret = lua2tree(); | ||
938 | lua_pop(L, 1); // -1 var | ||
939 | } | ||
940 | } | ||
941 | lua_pop(L, 1); // -1 chunk or error message | ||
942 | } | ||
943 | return ret; | ||
944 | } | ||
945 | |||
946 | void freeLuaTree(qtreetbl_t *tree) | ||
947 | { | ||
948 | qtreetbl_obj_t obj0; | ||
949 | |||
950 | memset((void*)&obj0, 0, sizeof(obj0)); // must be cleared before call | ||
951 | tree->lock(tree); // lock it when thread condition is expected | ||
952 | while(tree->getnext(tree, &obj0, false) == true) | ||
953 | { | ||
954 | qLua *q0 = obj0.data; | ||
955 | |||
956 | if (LUA_TTABLE == q0->type) | ||
957 | freeLuaTree(q0->v.t); | ||
958 | } | ||
959 | tree->unlock(tree); | ||
960 | tree->free(tree); | ||
961 | } | ||
962 | |||
963 | |||
964 | int Lua2hashtbl(char *file, qhashtbl_t *hash, char *var) | ||
965 | { | ||
966 | int ret = FALSE; | ||
967 | struct stat st; | ||
968 | |||
969 | if (0 != lstat(file, &st)) | ||
970 | { | ||
971 | D("No %s file.", file); | ||
972 | perror_msg("Unable to stat %s", file); | ||
973 | return FALSE; | ||
974 | } | ||
975 | else | ||
976 | { | ||
977 | int status = luaL_loadfile(L, file); // +1 the chunk or an error message. | ||
978 | if (status) // If something went wrong, error message is at the top of the stack. | ||
979 | E("Couldn't load Lua file: %s", lua_tostring(L, -1)); // 0 | ||
980 | else | ||
981 | { | ||
982 | int result = lua_pcall(L, 0, LUA_MULTRET, 0); // +1 result or an error message. Except we get 0 or error? | ||
983 | if (result) | ||
984 | { | ||
985 | E("Failed to run Lua script: %s", lua_tostring(L, -1)); // 0 | ||
986 | lua_pop(L, 1); // -1 chunk or error message | ||
987 | } | ||
988 | else | ||
989 | { | ||
990 | lua_getglobal(L, var); // +1 the value of var | ||
991 | lua_pushnil(L); // +1 nil, first key | ||
992 | while(lua_next(L, -2) != 0) // -1 key, +2 next key, value (or 0 if nothing left in table) | ||
993 | { | ||
994 | char *n = (char *) lua_tostring(L, -2); // 0 | ||
995 | |||
996 | // Numbers can convert to strings, so check for numbers before checking for strings. | ||
997 | // On the other hand, strings that can be converted to numbers also pass lua_isnumber(). sigh | ||
998 | if (lua_isnumber(L, -1)) // 0 | ||
999 | { | ||
1000 | float v = lua_tonumber(L, -1); // 0 | ||
1001 | hash->put(hash, n, &v, sizeof(float)); | ||
1002 | } | ||
1003 | else if (lua_isstring(L, -1)) // 0 | ||
1004 | hash->putstr(hash, n, (char *) lua_tostring(L, -1)); // 0 | ||
1005 | else if (lua_isboolean(L, -1)) // 0 | ||
1006 | { | ||
1007 | int v = lua_toboolean(L, -1); // 0 | ||
1008 | hash->putint(hash, n, v); | ||
1009 | } | ||
1010 | else | ||
1011 | { | ||
1012 | char *v = (char *) lua_tostring(L, -1); // 0 | ||
1013 | E("Unknown Lua variable type for %s = %s", n, v); | ||
1014 | } | ||
1015 | lua_pop(L, 1); // -1 value | ||
1016 | } | ||
1017 | lua_pop(L, 1); // -1 var | ||
1018 | ret = TRUE; | ||
1019 | } | ||
1020 | } | ||
1021 | } | ||
1022 | lua_pop(L, 1); // -1 chunk or error message | ||
1023 | return ret; | ||
1024 | } | ||
1025 | |||
1026 | |||
1027 | int LuaToHash(reqData *Rd, char *file, char *var, qhashtbl_t *tnm, int ret, struct stat *st, struct timespec *now, char *type) | ||
1028 | { | ||
1029 | struct timespec then; | ||
1030 | |||
1031 | if (-1 == clock_gettime(CLOCK_REALTIME, &then)) | ||
1032 | perror_msg("Unable to get the time."); | ||
1033 | I("Reading %s file %s", type, file); | ||
1034 | if (!Lua2hashtbl(file, tnm, var)) | ||
1035 | { | ||
1036 | bitch(Rd, "Broken thing.", "Can't run file."); | ||
1037 | ret++; | ||
1038 | } | ||
1039 | else | ||
1040 | { | ||
1041 | if (-1 == clock_gettime(CLOCK_REALTIME, now)) | ||
1042 | perror_msg("Unable to get the time."); | ||
1043 | double n = (now->tv_sec * 1000000000.0) + now->tv_nsec; | ||
1044 | double t = (then.tv_sec * 1000000000.0) + then.tv_nsec; | ||
1045 | t("Reading %s file took %lf seconds", type, (n - t) / 1000000000.0); | ||
1046 | } | ||
1047 | |||
1048 | return ret; | ||
1049 | } | ||
1050 | |||
1051 | |||
1052 | boolean writeLuaDouble(reqData *Rd, int fd, char *file, char *name, double number) | ||
1053 | { | ||
1054 | boolean ret = TRUE; | ||
1055 | // TODO - putting these in Lua as numbers causes lua_tolstring to barf when we read them. Though Lua is supposed to convert between numbers and strings. | ||
1056 | char *t = xmprintf(" ['%s'] = '%f',\n", name, number); // NOTE - default precision is 6 decimal places. | ||
1057 | size_t l = strlen(t); | ||
1058 | |||
1059 | if (l != writeall(fd, t, l)) | ||
1060 | { | ||
1061 | perror_msg("Writing %s", file); | ||
1062 | ret = FALSE; | ||
1063 | } | ||
1064 | free(t); | ||
1065 | return ret; | ||
1066 | } | ||
1067 | |||
1068 | boolean writeLuaInteger(reqData *Rd, int fd, char *file, char *name, long number) | ||
1069 | { | ||
1070 | boolean ret = TRUE; | ||
1071 | // TODO - putting these in Lua as numbers causes lua_tolstring to barf when we read them. Though Lua is supposed to convert between numbers and strings. | ||
1072 | char *t = xmprintf(" ['%s'] = '%ld',\n", name, number); | ||
1073 | size_t l = strlen(t); | ||
1074 | |||
1075 | if (l != writeall(fd, t, l)) | ||
1076 | { | ||
1077 | perror_msg("Writing %s", file); | ||
1078 | ret = FALSE; | ||
1079 | } | ||
1080 | free(t); | ||
1081 | return ret; | ||
1082 | } | ||
1083 | |||
1084 | boolean writeLuaString(reqData *Rd, int fd, char *file, char *name, char *string) | ||
1085 | { | ||
1086 | boolean ret = TRUE; | ||
1087 | |||
1088 | if (NULL == string) | ||
1089 | string = getStrH(Rd->stuff, name); | ||
1090 | |||
1091 | size_t l = strlen(string); | ||
1092 | char *t0 = xmalloc(l * 2 + 1); | ||
1093 | int i, j = 0; | ||
1094 | |||
1095 | // TODO - maybe escape other non printables as well? | ||
1096 | for (i = 0; i < l; i++) | ||
1097 | { | ||
1098 | // We don't need to escape [] here, coz we are using '' below. Same applies to ", but do it anyway. | ||
1099 | switch(string[i]) | ||
1100 | { | ||
1101 | case '\n': | ||
1102 | case '\\': | ||
1103 | case '\'': | ||
1104 | case '"': | ||
1105 | t0[j++] = '\\'; break; | ||
1106 | } | ||
1107 | if ('\n' == string[i]) | ||
1108 | t0[j++] = 'n'; | ||
1109 | else if ('\r' == string[i]) | ||
1110 | ; | ||
1111 | else | ||
1112 | t0[j++] = string[i]; | ||
1113 | } | ||
1114 | t0[j] = '\0'; | ||
1115 | |||
1116 | char *t1 = xmprintf(" ['%s'] = '%s',\n", name, t0); | ||
1117 | |||
1118 | l = strlen(t1); | ||
1119 | if (l != writeall(fd, t1, l)) | ||
1120 | { | ||
1121 | perror_msg("Writing %s to %s", name, file); | ||
1122 | ret = FALSE; | ||
1123 | } | ||
1124 | free(t1); | ||
1125 | free(t0); | ||
596 | return ret; | 1126 | return ret; |
597 | } | 1127 | } |
598 | 1128 | ||
1129 | |||
1130 | void doTmuxCmd(char *format, ...) | ||
1131 | { | ||
1132 | va_list va, va2; | ||
1133 | int len; | ||
1134 | char *ret; | ||
1135 | |||
1136 | va_start(va, format); | ||
1137 | va_copy(va2, va); | ||
1138 | // How long is it? | ||
1139 | len = vsnprintf(0, 0, format, va); | ||
1140 | len++; | ||
1141 | va_end(va); | ||
1142 | // Allocate and do the sprintf() | ||
1143 | ret = xmalloc(len); | ||
1144 | vsnprintf(ret, len, format, va2); | ||
1145 | va_end(va2); | ||
1146 | |||
1147 | if (shellMeFail("%s %s/%s %s", Tcmd, scRun, Tsocket, ret)) | ||
1148 | E("tmux command failed! %s", ret); | ||
1149 | free(ret); | ||
1150 | } | ||
1151 | |||
1152 | void sendTmuxCmd(char *dest, char *cmd) | ||
1153 | { | ||
1154 | doTmuxCmd("send-keys -t %s:'%s' '%s' Enter", Tconsole, dest, cmd); | ||
1155 | } | ||
1156 | |||
1157 | void sendTmuxKeys(char *dest, char *keys) | ||
1158 | { | ||
1159 | doTmuxCmd("send-keys -t %s:%s '%s'", Tconsole, dest, keys); | ||
1160 | } | ||
1161 | |||
599 | void waitTmuxText(char *dest, char *text) | 1162 | void waitTmuxText(char *dest, char *text) |
600 | { | 1163 | { |
601 | int i; | 1164 | int i; |
602 | // Using " for the grep pattern, coz ' might be used in a sim name. | 1165 | // Using " for the grep pattern, coz ' might be used in a sim name. |
603 | // TODO - should escape \ " ` in text. | 1166 | // TODO - should escape \ " ` in text. |
604 | char *c = xmprintf("sleep 5; %s %s/%s capture-pane -t %s:'%s' -p | grep -F \"%s\" 2>&1 > /dev/null", Tcmd, scRun, Tsocket, Tconsole, dest, text); | 1167 | char *c = xmprintf("sleep 1; %s %s/%s capture-pane -Jt %s -p | grep -E \"%s\" 2>&1 > /dev/null", Tcmd, scRun, Tsocket, dest, text); |
605 | 1168 | ||
606 | D("Waiting for '%s'.", text); | 1169 | T("Waiting for '%s'.", text); |
607 | do | 1170 | do |
608 | { | 1171 | { |
609 | i = system(c); | 1172 | i = system(c); |
@@ -647,12 +1210,44 @@ float waitLoadAverage(float la, float extra, int timeout) | |||
647 | // Rob forget to do this, but at least he didn't declare it static. | 1210 | // Rob forget to do this, but at least he didn't declare it static. |
648 | struct dirtree *dirtree_handle_callback(struct dirtree *new, int (*callback)(struct dirtree *node)); | 1211 | struct dirtree *dirtree_handle_callback(struct dirtree *new, int (*callback)(struct dirtree *node)); |
649 | 1212 | ||
1213 | |||
1214 | // A sim structure for holding all the stuff from the sim.ini file. | ||
1215 | typedef struct _simData simData; | ||
1216 | struct _simData | ||
1217 | { | ||
1218 | // portH is the HTTP port for the sim, portU is the UDP port for the sim. | ||
1219 | int locX, locY, sizeX, sizeY, sizeZ, portH, portU, maxPrims; | ||
1220 | char *name, *tab, *UUID, *regionType, *estate, *owner; | ||
1221 | char *paneID; | ||
1222 | int window, pane, users; | ||
1223 | }; | ||
1224 | |||
650 | typedef struct _simList simList; | 1225 | typedef struct _simList simList; |
651 | struct _simList | 1226 | struct _simList |
652 | { | 1227 | { |
653 | int len, num; | 1228 | int len, num; |
654 | char **sims; | 1229 | char **sims; |
1230 | qtreetbl_t *byTab, *simsLua, *unsorted, *byUUID; | ||
1231 | // Stuff for the looping through sims, doing things, and waiting. | ||
1232 | int doIt; | ||
1233 | float la; | ||
1234 | char *target, *backup; | ||
655 | }; | 1235 | }; |
1236 | simList *ourSims = NULL; | ||
1237 | |||
1238 | |||
1239 | static int getIntFromIni(qlisttbl_t *ini, char *name) | ||
1240 | { | ||
1241 | int ret; | ||
1242 | char *t = "0"; | ||
1243 | |||
1244 | t = ini->getstr(ini, name, false); | ||
1245 | if (NULL == t) | ||
1246 | t = "0"; | ||
1247 | else if ('"' == t[0]) | ||
1248 | t = qstrunchar(t, '"', '"'); | ||
1249 | return strtol(t, NULL, 10); | ||
1250 | } | ||
656 | 1251 | ||
657 | static int filterSims(struct dirtree *node) | 1252 | static int filterSims(struct dirtree *node) |
658 | { | 1253 | { |
@@ -672,6 +1267,7 @@ static int filterSims(struct dirtree *node) | |||
672 | return 0; | 1267 | return 0; |
673 | } | 1268 | } |
674 | 1269 | ||
1270 | /* | ||
675 | // We particularly don't want \ " ` | 1271 | // We particularly don't want \ " ` |
676 | char *cleanSimName(char *name) | 1272 | char *cleanSimName(char *name) |
677 | { | 1273 | { |
@@ -690,91 +1286,441 @@ char *cleanSimName(char *name) | |||
690 | 1286 | ||
691 | return ret; | 1287 | return ret; |
692 | } | 1288 | } |
1289 | */ | ||
693 | 1290 | ||
694 | simList *getSims() | 1291 | static int filterInis(struct dirtree *node) |
695 | { | 1292 | { |
696 | simList *sims = xmalloc(sizeof(simList)); | 1293 | if (!node->parent) return DIRTREE_RECURSE | DIRTREE_SHUTUP; |
697 | memset(sims, 0, sizeof(simList)); | 1294 | int l = strlen(node->name); |
698 | char *path = xmprintf("%s/config", scRoot); | 1295 | if (strncmp(&(node->name[l - 4]), ".ini", 4) == 0) |
699 | struct dirtree *new = dirtree_add_node(0, path, 0); | 1296 | { |
700 | new->extra = (long) sims; | 1297 | strncpy((char *) node->parent->extra, node->name, l - 4); |
701 | dirtree_handle_callback(new, filterSims); | 1298 | return DIRTREE_ABORT; |
1299 | } | ||
1300 | return 0; | ||
1301 | } | ||
1302 | |||
1303 | static int filterShinis(struct dirtree *node) | ||
1304 | { | ||
1305 | if (!node->parent) return DIRTREE_RECURSE | DIRTREE_SHUTUP; | ||
1306 | int l = strlen(node->name); | ||
1307 | if ((6 < l) && ((strncmp(&(node->name[l - 6]), ".shini", 6) == 0))) | ||
1308 | { | ||
1309 | simList *list = (simList *) node->parent->extra; | ||
1310 | |||
1311 | if ((list->num + 1) > list->len) | ||
1312 | { | ||
1313 | list->len = list->len + 1; | ||
1314 | list->sims = xrealloc(list->sims, list->len * sizeof(char *)); | ||
1315 | } | ||
1316 | list->sims[list->num] = xstrndup(node->name, l - 6); | ||
1317 | list->num++; | ||
1318 | } | ||
1319 | return 0; | ||
1320 | } | ||
1321 | |||
1322 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
1323 | // Sim wrangling loop. | ||
1324 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
1325 | typedef void (*simFunction)(simData *simd, char *sim, char *type, int count, int window, int panes, int pane); | ||
1326 | void forEachSim(char *verb, simFunction func, simFunction not) | ||
1327 | { | ||
1328 | qtreetbl_obj_t obj0, obj1; | ||
1329 | qLua *q0, *q1; | ||
1330 | int count = 0, window = 0, panes = 4, pane = 0; | ||
1331 | |||
1332 | memset((void*)&obj0, 0, sizeof(obj0)); | ||
1333 | ourSims->simsLua->lock(ourSims->simsLua); | ||
1334 | while(ourSims->simsLua->getnext(ourSims->simsLua, &obj0, false) == true) | ||
1335 | { | ||
1336 | q0 = obj0.data; | ||
1337 | char *type = "unsorted"; | ||
1338 | |||
1339 | if (LUA_TTABLE == q0->type) | ||
1340 | { | ||
1341 | panes = 4; | ||
1342 | q1 = q0->v.t->get(q0->v.t, "number", NULL, false); if (NULL != q1) window = q1->v.f - 1; | ||
1343 | q1 = q0->v.t->get(q0->v.t, "panes", NULL, false); if (NULL != q1) panes = q1->v.f; | ||
1344 | q1 = q0->v.t->get(q0->v.t, "type", NULL, false); if (NULL != q1) type = q1->v.s; | ||
1345 | if (0 == panes) | ||
1346 | { | ||
1347 | pane = 3; | ||
1348 | window = 0; | ||
1349 | type = Ttab; | ||
1350 | } | ||
1351 | else if (0 != pane) | ||
1352 | { | ||
1353 | pane = 0; | ||
1354 | window++; | ||
1355 | } | ||
1356 | |||
1357 | if (verb) | ||
1358 | V("%s sims of type %s, window %d, %d panes per window.", verb, type, window, panes); | ||
1359 | memset((void*)&obj1, 0, sizeof(obj1)); | ||
1360 | q0->v.t->lock(q0->v.t); | ||
1361 | while(q0->v.t->getnext(q0->v.t, &obj1, false) == true) | ||
1362 | { | ||
1363 | q1 = obj1.data; | ||
1364 | |||
1365 | if ((strcmp("number", obj1.name) != 0) && (strcmp("panes", obj1.name) != 0) && (strcmp("type", obj1.name) != 0)) | ||
1366 | { | ||
1367 | simData *simd = ourSims->byTab->get(ourSims->byTab, q1->v.s, NULL, false); | ||
702 | 1368 | ||
703 | qsort(sims->sims, sims->num, sizeof(char *), qstrcmp); | 1369 | if (NULL == simd) |
1370 | { | ||
1371 | if (not) | ||
1372 | not(simd, q1->v.s, type, count, window, panes, pane); | ||
1373 | else | ||
1374 | E("Sim %s not found in ini list!", q1->v.s); | ||
1375 | } | ||
1376 | else | ||
1377 | { | ||
1378 | func(simd, q1->v.s, type, count, window, panes, pane); | ||
1379 | count++; | ||
1380 | pane++; | ||
1381 | if (pane >= panes) | ||
1382 | { | ||
1383 | pane = 0; | ||
1384 | window++; | ||
1385 | } | ||
1386 | } | ||
1387 | } | ||
1388 | } | ||
1389 | q0->v.t->unlock(q0->v.t); | ||
1390 | } | ||
1391 | } | ||
1392 | ourSims->simsLua->unlock(ourSims->simsLua); | ||
1393 | } | ||
1394 | |||
1395 | void simNotFound(simData *simd, char *sim, char *type, int count, int window, int panes, int pane) | ||
1396 | { | ||
1397 | char *path = xmprintf("%s/%s.shini", scEtc, sim); | ||
1398 | qlisttbl_t *ini = qconfig_parse_file(NULL, path, '='); | ||
1399 | |||
1400 | if (NULL == ini) | ||
1401 | E("Can't find %s", path); | ||
1402 | else | ||
1403 | { | ||
1404 | simd = xzalloc(sizeof(simData)); | ||
1405 | simd->tab = sim; | ||
1406 | simd->name = qstrunchar(ini->getstr(ini, "Region.RegionName", true), '"', '"'); | ||
1407 | simd->UUID = qstrunchar(ini->getstr(ini, "Region.RegionUUID", true), '"', '"'); | ||
1408 | // simd->regionType = qstrunchar(ini->getstr(ini, "Region.RegionType", true), '"', '"'); | ||
1409 | simd->sizeX = getIntFromIni(ini, "Region.SizeX"); | ||
1410 | simd->sizeY = getIntFromIni(ini, "Region.SizeY"); | ||
1411 | simd->sizeZ = getIntFromIni(ini, "Region.SizeZ"); | ||
1412 | // TODO - store a pointer instead of multiple copies of the data. | ||
1413 | ourSims->byTab->put(ourSims->byTab, sim, simd, sizeof(simData)); | ||
1414 | ourSims->byUUID->put(ourSims->byUUID, simd->UUID, simd, sizeof(simData)); | ||
1415 | if (strcmp("unsorted", type) == 0) | ||
1416 | ourSims->unsorted->put(ourSims->unsorted, sim, simd, sizeof(simData)); | ||
1417 | ini->free(ini); | ||
1418 | free(simd); | ||
1419 | } | ||
704 | free(path); | 1420 | free(path); |
705 | return sims; | ||
706 | } | 1421 | } |
707 | 1422 | ||
1423 | |||
708 | void freeSimList(simList *sims) | 1424 | void freeSimList(simList *sims) |
709 | { | 1425 | { |
710 | int i; | 1426 | int i; |
711 | 1427 | ||
1428 | if (NULL == sims) | ||
1429 | return; | ||
1430 | |||
712 | for (i = 0; i < sims->num; i++) | 1431 | for (i = 0; i < sims->num; i++) |
713 | free(sims->sims[i]); | 1432 | free(sims->sims[i]); |
714 | free(sims->sims); | 1433 | free(sims->sims); |
1434 | |||
1435 | qtreetbl_obj_t obj; | ||
1436 | memset((void*) &obj, 0, sizeof(obj)); // start from the minimum. | ||
1437 | sims->byTab->lock(sims->byTab); | ||
1438 | while (sims->byTab->getnext(sims->byTab, &obj, false) == true) | ||
1439 | { | ||
1440 | char *name = qmemdup(obj.name, obj.namesize); // keep the name | ||
1441 | size_t namesize = obj.namesize; // for removal argument | ||
1442 | { | ||
1443 | simData *simd = (simData *) obj.data; | ||
1444 | |||
1445 | if (NULL != simd) | ||
1446 | { | ||
1447 | free(simd->name); | ||
1448 | // free(simd->tab); | ||
1449 | free(simd->UUID); | ||
1450 | // free(simd->regionType); | ||
1451 | free(simd->estate); | ||
1452 | free(simd->owner); | ||
1453 | free(simd->paneID); | ||
1454 | } | ||
1455 | } | ||
1456 | // sims->byTab->remove_by_obj(sims->byTab, obj.name, obj.namesize); // remove | ||
1457 | // obj = sims->byTab->find_nearest(sims->byTab, name, namesize, false); // rewind one step back | ||
1458 | free(name); // clean up | ||
1459 | } | ||
1460 | sims->byTab->unlock(sims->byTab); | ||
1461 | sims->byTab->free(sims->byTab); | ||
1462 | freeLuaTree(sims->simsLua); | ||
1463 | freeLuaTree(sims->unsorted); | ||
1464 | freeLuaTree(sims->byUUID); | ||
1465 | free(sims->target); | ||
1466 | free(sims->backup); | ||
1467 | |||
715 | free(sims); | 1468 | free(sims); |
716 | } | 1469 | } |
717 | 1470 | ||
718 | static int filterInis(struct dirtree *node) | 1471 | // Get the details from all the .ini or .shini sim files. |
1472 | // Creates the .shini and sims.lua files if it's an old install. | ||
1473 | simList *getSims() | ||
719 | { | 1474 | { |
720 | if (!node->parent) return DIRTREE_RECURSE | DIRTREE_SHUTUP; | 1475 | if (NULL != ourSims) return ourSims; |
721 | int l = strlen(node->name); | 1476 | |
722 | if (strncmp(&(node->name[l - 4]), ".ini", 4) == 0) | 1477 | int i, fd = -1; |
1478 | size_t l; | ||
1479 | char *file = xmprintf("%s/sims.lua", scEtc); | ||
1480 | |||
1481 | ourSims = xzalloc(sizeof(simList)); | ||
1482 | ourSims->byTab = qtreetbl(0); | ||
1483 | ourSims->unsorted = qtreetbl(0); | ||
1484 | ourSims->byUUID = qtreetbl(0); | ||
1485 | ourSims->doIt = FALSE; | ||
1486 | |||
1487 | // Read or create simsLua | ||
1488 | ourSims->simsLua = Lua2tree(file, "sims"); | ||
1489 | if (NULL == ourSims->simsLua) | ||
1490 | { | ||
1491 | ourSims->simsLua = qtreetbl(0); | ||
1492 | ourSims->doIt = TRUE; | ||
1493 | } | ||
1494 | |||
1495 | // Find all the old .ini files, convert them to .shini files if they don't exist. | ||
1496 | char *path = xmprintf("%s/config", scRoot), *newPath; | ||
1497 | struct dirtree *new = dirtree_add_node(0, path, 0); | ||
1498 | |||
1499 | new->extra = (long) ourSims; | ||
1500 | dirtree_handle_callback(new, filterSims); | ||
1501 | free(path); | ||
1502 | for (i = 0; i < ourSims->num; i++) | ||
1503 | { | ||
1504 | char *sim = ourSims->sims[i], *name = xmprintf("%s/config/%s", scRoot, sim); | ||
1505 | qlisttbl_t *ini; | ||
1506 | struct dirtree *new = dirtree_add_node(0, name, 0); | ||
1507 | |||
1508 | free(name); | ||
1509 | name = xzalloc(1024); | ||
1510 | new->extra = (long) name; | ||
1511 | dirtree_handle_callback(new, filterInis); | ||
1512 | if ('\0' != name[0]) | ||
723 | { | 1513 | { |
724 | strcpy((char *) node->parent->extra, node->name); | 1514 | path = xmprintf("%s/config/%s/%s.ini", scRoot, sim, name); |
725 | return DIRTREE_ABORT; | 1515 | newPath = xmprintf("%s/%s.shini", scEtc, name); |
1516 | |||
1517 | if (!qfile_exist(newPath)) | ||
1518 | { | ||
1519 | char *cmd = xmprintf("sed -E" | ||
1520 | " -e 's#\\[Const]#\\[Const] ; fakeVariableCozOpenSim='' ; pushd ../current/bin; ./sledjchisl $1 `basename $0`; popd ; exit 0#'" | ||
1521 | " -e 's/mysim=\"[[:digit:]]*\"/mysim=\"%s\"/'" | ||
1522 | " -e 's/sim\\$\\{Const\\|mysim\\}/\\$\\{Const\\|mysim\\}/g'" | ||
1523 | " -e '/\\[Startup\\]/d'" | ||
1524 | " -e '/PIDFile.*/d'" | ||
1525 | " -e '/LogFile.*/d'" | ||
1526 | " -e '/StatsLogFile.*/d'" | ||
1527 | " -e '/ConsoleHistoryFile.*/d'" | ||
1528 | " %s >%s", name, path, newPath); | ||
1529 | |||
1530 | V("Converting %s.ini -> %s", path, newPath); | ||
1531 | D(cmd); | ||
1532 | if (shellMeFail(cmd)) | ||
1533 | E("sed command failed!"); | ||
1534 | else | ||
1535 | { | ||
1536 | if (shellMeFail("chmod ugo+x %s", newPath)) | ||
1537 | E("chmod command failed!"); | ||
1538 | |||
1539 | char *link = xmprintf("%s/%s.shini", scBin, name); | ||
1540 | I("Symlinking %s to %s", newPath, link); | ||
1541 | if (qfile_exist(link)) | ||
1542 | { | ||
1543 | if (shellMeFail("rm %s", link)) | ||
1544 | E("rm command failed!"); | ||
1545 | } | ||
1546 | if (0 != symlink(newPath, link)) | ||
1547 | perror_msg("Symlinking %s to %s", newPath, link); | ||
1548 | free(link); | ||
1549 | } | ||
1550 | free(cmd); | ||
1551 | } | ||
1552 | free(newPath); | ||
1553 | free(path); | ||
726 | } | 1554 | } |
727 | return 0; | 1555 | free(name); |
728 | } | 1556 | } |
1557 | // dumpLuaTree(ourSims->simsLua, "simsLua", 0); | ||
1558 | // dumpLuaTree(ourSims->byTab, "byTab", 0); | ||
1559 | |||
1560 | // Read all the .shini files. | ||
1561 | forEachSim(NULL, NULL, simNotFound); | ||
1562 | // dumpLuaTree(ourSims->simsLua, "simsLua", 0); | ||
1563 | // dumpLuaTree(ourSims->byTab, "byTab", 0); | ||
1564 | // dumpLuaTree(ourSims->unsorted, "unsorted", 0); | ||
1565 | |||
1566 | // Check for any .shini files not in sims.lua, add them to the unsorted list. | ||
1567 | for (i = 0; i < ourSims->num; i++) | ||
1568 | free(ourSims->sims[i]); | ||
1569 | ourSims->num = 0; | ||
1570 | new = dirtree_add_node(0, scEtc, 0); | ||
1571 | new->extra = (long) ourSims; | ||
1572 | dirtree_handle_callback(new, filterShinis); | ||
1573 | for (i = 0; i < ourSims->num; i++) | ||
1574 | { | ||
1575 | simData *simd = ourSims->byTab->get(ourSims->byTab, ourSims->sims[i], NULL, false); | ||
1576 | if (NULL == simd) | ||
1577 | { | ||
1578 | V("Shini NOT found in list - %s/%s.shini", scEtc, ourSims->sims[i]); | ||
1579 | simNotFound(simd, ourSims->sims[i], "unsorted", 0, 0, 0, 0); | ||
1580 | ourSims->doIt = TRUE; | ||
1581 | } | ||
1582 | } | ||
1583 | // dumpLuaTree(ourSims->simsLua, "simsLua", 0); | ||
1584 | // dumpLuaTree(ourSims->byTab, "byTab", 0); | ||
1585 | // dumpLuaTree(ourSims->unsorted, "unsorted", 0); | ||
729 | 1586 | ||
730 | char *getSimName(char *sim) | 1587 | // Write the sims.lua_NEW file if needed. |
731 | { | 1588 | if (ourSims->doIt) |
732 | char *ret = NULL; | 1589 | { |
733 | char *c = xmprintf("%s/config/%s", scRoot, sim); | 1590 | char *tnm; |
734 | struct dirtree *new = dirtree_add_node(0, c, 0); | ||
735 | 1591 | ||
736 | free(c); | 1592 | if (shellMeFail("mv -f %s/sims.lua %s/sims.lua.BACKUP", scEtc, scEtc)) |
737 | c = xzalloc(1024); | 1593 | E("mv command failed!"); |
738 | new->extra = (long) c; | 1594 | |
739 | dirtree_handle_callback(new, filterInis); | 1595 | free(file); |
740 | if ('\0' != c[0]) | 1596 | file = xmprintf("%s/sims.lua", scEtc); |
741 | { | 1597 | I("Creating sims %s.", file); |
742 | char *temp = NULL; | 1598 | |
743 | regex_t pat; | 1599 | fd = notstdio(xcreate_stdio(file, O_CREAT | O_WRONLY | O_TRUNC | O_CLOEXEC, S_IRUSR | S_IWUSR)); |
744 | regmatch_t m[2]; | 1600 | if (-1 != fd) |
745 | long len; | 1601 | { |
746 | int fd; | 1602 | tnm = "sims = -- Note these are .shini / tmux tab short names.\n{\n"; |
747 | 1603 | l = strlen(tnm); | |
748 | temp = xmprintf("%s/config/%s/%s", scRoot, sim, c); | 1604 | if (l != writeall(fd, tnm, l)) |
749 | fd = xopenro(temp); | 1605 | perror_msg("Writing %s", file); |
750 | xregcomp(&pat, "RegionName = \"(.+)\"", REG_EXTENDED); | 1606 | |
751 | do | 1607 | qtreetbl_obj_t obj0, obj1; |
752 | { | 1608 | qLua *q0, *q1; |
753 | // TODO - get_line() is slow, and wont help much with DOS and Mac line endings. | 1609 | |
754 | // gio_gets() isn't any faster really, but deals with DOS line endings at least. | 1610 | memset((void*)&obj0, 0, sizeof(obj0)); |
755 | free(temp); | 1611 | ourSims->simsLua->lock(ourSims->simsLua); |
756 | temp = get_line(fd); | 1612 | while(ourSims->simsLua->getnext(ourSims->simsLua, &obj0, false) == true) |
757 | if (temp) | ||
758 | { | 1613 | { |
759 | if (!regexec(&pat, temp, 2, m, 0)) | 1614 | q0 = obj0.data; |
1615 | char *type = "unsorted"; | ||
1616 | |||
1617 | if (LUA_TTABLE == q0->type) | ||
760 | { | 1618 | { |
761 | // Return first parenthesized subexpression as string. | 1619 | tnm = " {"; |
762 | if (pat.re_nsub > 0) | 1620 | l = strlen(tnm); |
1621 | if (l != writeall(fd, tnm, l)) | ||
1622 | perror_msg("Writing %s", file); | ||
1623 | q1 = q0->v.t->get(q0->v.t, "type", NULL, false); | ||
1624 | if (NULL != q1) | ||
1625 | { | ||
1626 | type = q1->v.s; | ||
1627 | tnm = xmprintf("['type'] = '%s'; ", type); | ||
1628 | l = strlen(tnm); | ||
1629 | if (l != writeall(fd, tnm, l)) | ||
1630 | perror_msg("Writing %s", file); | ||
1631 | free(tnm); | ||
1632 | } | ||
1633 | q1 = q0->v.t->get(q0->v.t, "number", NULL, false); | ||
1634 | if (NULL != q1) | ||
1635 | { | ||
1636 | tnm = xmprintf("['number'] = %d; ", (int) q1->v.f); | ||
1637 | l = strlen(tnm); | ||
1638 | if (l != writeall(fd, tnm, l)) | ||
1639 | perror_msg("Writing %s", file); | ||
1640 | free(tnm); | ||
1641 | } | ||
1642 | q1 = q0->v.t->get(q0->v.t, "panes", NULL, false); | ||
1643 | if (NULL != q1) | ||
1644 | { | ||
1645 | tnm = xmprintf("['panes'] = %d; ", (int) q1->v.f); | ||
1646 | l = strlen(tnm); | ||
1647 | if (l != writeall(fd, tnm, l)) | ||
1648 | perror_msg("Writing %s", file); | ||
1649 | free(tnm); | ||
1650 | } | ||
1651 | |||
1652 | if (strcmp("unsorted", type) != 0) | ||
763 | { | 1653 | { |
764 | ret = xmprintf("%.*s", (int) (m[1].rm_eo - m[1].rm_so), temp + m[1].rm_so); | 1654 | memset((void*)&obj1, 0, sizeof(obj1)); |
765 | free(temp); | 1655 | q0->v.t->lock(q0->v.t); |
766 | break; | 1656 | while(q0->v.t->getnext(q0->v.t, &obj1, false) == true) |
1657 | { | ||
1658 | q1 = obj1.data; | ||
1659 | |||
1660 | if ((strcmp("number", obj1.name) != 0) && (strcmp("panes", obj1.name) != 0) && (strcmp("type", obj1.name) != 0)) | ||
1661 | { | ||
1662 | tnm = xmprintf("\n '%s',", q1->v.s); | ||
1663 | l = strlen(tnm); | ||
1664 | if (l != writeall(fd, tnm, l)) | ||
1665 | perror_msg("Writing %s", file); | ||
1666 | free(tnm); | ||
1667 | } | ||
1668 | } | ||
1669 | q0->v.t->unlock(q0->v.t); | ||
767 | } | 1670 | } |
768 | } | 1671 | else |
1672 | { | ||
1673 | memset((void*)&obj1, 0, sizeof(obj1)); | ||
1674 | ourSims->unsorted->lock(ourSims->unsorted); | ||
1675 | while(ourSims->unsorted->getnext(ourSims->unsorted, &obj1, false) == true) | ||
1676 | { | ||
1677 | simData *simd = obj1.data; | ||
1678 | |||
1679 | if ((strcmp("number", obj1.name) != 0) && (strcmp("panes", obj1.name) != 0) && (strcmp("type", obj1.name) != 0)) | ||
1680 | { | ||
1681 | tnm = xmprintf("\n '%s',", simd->tab); | ||
1682 | l = strlen(tnm); | ||
1683 | if (l != writeall(fd, tnm, l)) | ||
1684 | perror_msg("Writing %s", file); | ||
1685 | free(tnm); | ||
1686 | } | ||
1687 | } | ||
1688 | ourSims->unsorted->unlock(ourSims->unsorted); | ||
1689 | } | ||
1690 | } | ||
1691 | tnm = "\n },\n"; | ||
1692 | l = strlen(tnm); | ||
1693 | if (l != writeall(fd, tnm, l)) | ||
1694 | perror_msg("Writing %s", file); | ||
769 | } | 1695 | } |
770 | } while (temp); | 1696 | ourSims->simsLua->unlock(ourSims->simsLua); |
771 | regfree(&pat); | 1697 | tnm = "}\nreturn sims\n"; |
772 | xclose(fd); | 1698 | l = strlen(tnm); |
1699 | if (l != writeall(fd, tnm, l)) | ||
1700 | perror_msg("Writing %s", file); | ||
1701 | |||
1702 | xclose(fd); | ||
1703 | } | ||
1704 | freeSimList(ourSims); | ||
1705 | free(file); | ||
1706 | // Reread everything. | ||
1707 | file = xmprintf("%s/sims.lua", scEtc); | ||
1708 | ourSims = xzalloc(sizeof(simList)); | ||
1709 | ourSims->byTab = qtreetbl(0); | ||
1710 | ourSims->unsorted = qtreetbl(0); | ||
1711 | ourSims->doIt = FALSE; | ||
1712 | ourSims->simsLua = Lua2tree(file, "sims"); | ||
1713 | forEachSim(NULL, NULL, simNotFound); | ||
1714 | // dumpLuaTree(ourSims->simsLua, "simsLua", 0); | ||
1715 | // dumpLuaTree(ourSims->byTab, "byTab", 0); | ||
1716 | // dumpLuaTree(ourSims->unsorted, "unsorted", 0); | ||
773 | } | 1717 | } |
774 | free(c); | 1718 | free(file); |
775 | return ret; | ||
776 | } | ||
777 | 1719 | ||
1720 | // dumpLuaTree(ourSims->simsLua, "simsLua", 0); | ||
1721 | // dumpLuaTree(ourSims->byTab, "byTab", 0); | ||
1722 | return ourSims; | ||
1723 | } | ||
778 | 1724 | ||
779 | // Expects either "simXX" or "ROBUST". | 1725 | // Expects either "simXX" or "ROBUST". |
780 | int checkSimIsRunning(char *sim) | 1726 | int checkSimIsRunning(char *sim) |
@@ -783,7 +1729,7 @@ int checkSimIsRunning(char *sim) | |||
783 | struct stat st; | 1729 | struct stat st; |
784 | 1730 | ||
785 | // Check if it's running. | 1731 | // Check if it's running. |
786 | char *path = xmprintf("%s/caches/%s.pid", scRoot, sim); | 1732 | char *path = xmprintf("%s/%s.pid", scRun, sim); |
787 | if (0 == stat(path, &st)) | 1733 | if (0 == stat(path, &st)) |
788 | { | 1734 | { |
789 | int fd, i; | 1735 | int fd, i; |
@@ -804,20 +1750,27 @@ int checkSimIsRunning(char *sim) | |||
804 | // I'd rather re-use the toysh command running stuff, since ps is a toy, but that's private. | 1750 | // I'd rather re-use the toysh command running stuff, since ps is a toy, but that's private. |
805 | // TODO - switch to toybox ps and rm. | 1751 | // TODO - switch to toybox ps and rm. |
806 | free(path); | 1752 | free(path); |
807 | path = xmprintf("ps -p %s --no-headers -o comm", pid); | 1753 | path = xmprintf("ps -p %s --no-headers -o comm >/dev/null", pid); |
808 | i = system(path); | 1754 | i = system(path); |
809 | if (WIFEXITED(i)) | 1755 | if (WIFEXITED(i)) |
810 | { | 1756 | { |
811 | if (0 != WEXITSTATUS(i)) // No such pid. | 1757 | if (0 != WEXITSTATUS(i)) // No such pid. |
812 | { | 1758 | { |
813 | free(path); | 1759 | free(path); |
814 | path = xmprintf("rm -f %s/caches/%s.pid", scRoot, sim); | 1760 | path = xmprintf("rm -f %s/%s.pid", scRun, sim); |
815 | d("%s", path); | 1761 | d("%s", path); |
816 | i = system(path); | 1762 | i = system(path); |
1763 | if (WIFEXITED(i)) | ||
1764 | { | ||
1765 | if (0 != WEXITSTATUS(i)) // No such pid. | ||
1766 | E("Cannot remove stale PID file %s", path); | ||
1767 | } | ||
817 | } | 1768 | } |
818 | else | 1769 | else |
819 | d("checkSimIsRunning(%s) has PID %s, which is actually running.", sim, pid); | 1770 | d("checkSimIsRunning(%s) has PID %s, which is actually running.", sim, pid); |
820 | } | 1771 | } |
1772 | // TODO - WTF? | ||
1773 | // free(path); | ||
821 | } | 1774 | } |
822 | } | 1775 | } |
823 | free(pid); | 1776 | free(pid); |
@@ -825,7 +1778,7 @@ int checkSimIsRunning(char *sim) | |||
825 | 1778 | ||
826 | // Now check if it's really really running. lol | 1779 | // Now check if it's really really running. lol |
827 | free(path); | 1780 | free(path); |
828 | path = xmprintf("%s/caches/%s.pid", scRoot, sim); | 1781 | path = xmprintf("%s/%s.pid", scRun, sim); |
829 | if (0 == stat(path, &st)) | 1782 | if (0 == stat(path, &st)) |
830 | { | 1783 | { |
831 | D("checkSimIsRunning(%s) -> %s is really really running.", sim, path); | 1784 | D("checkSimIsRunning(%s) -> %s is really really running.", sim, path); |
@@ -838,6 +1791,581 @@ int checkSimIsRunning(char *sim) | |||
838 | return ret; | 1791 | return ret; |
839 | } | 1792 | } |
840 | 1793 | ||
1794 | void findWindow(simData *simd, char *sim, char *type, int count, int window, int panes, int pane) | ||
1795 | { | ||
1796 | snprintf(toybuf, sizeof(toybuf), "%s/IDs_%d.lua", scTemp, window); | ||
1797 | qtreetbl_t *IDs = Lua2tree(toybuf, "IDs"); | ||
1798 | snprintf(toybuf, sizeof(toybuf), "%d %d", window, pane); | ||
1799 | free(simd->paneID); | ||
1800 | simd->paneID = xmprintf("%s", qLuaGet(IDs, toybuf)->v.s); | ||
1801 | freeLuaTree(IDs); | ||
1802 | if (strcmp(backupIARsim, sim) == 0) | ||
1803 | { | ||
1804 | ourSims->backup = xstrdup(simd->paneID); | ||
1805 | V("Found backup sim %s in %s", sim, simd->paneID); | ||
1806 | } | ||
1807 | } | ||
1808 | |||
1809 | void prepSims(simData *simd, char *sim, char *type, int count, int window, int panes, int pane) | ||
1810 | { | ||
1811 | char *path = xmprintf("%s/%s.shini", scEtc, simd->tab); | ||
1812 | char *newPath = xmprintf("%s/sim%d/%s.ini", scTemp, count, simd->tab); // Coz OpenSim sucks. | ||
1813 | char *cmd; | ||
1814 | |||
1815 | simd->window = window; | ||
1816 | simd->pane = pane; | ||
1817 | |||
1818 | // Make sure all tmux windows and panes are startned. | ||
1819 | if (0 == panes) | ||
1820 | { | ||
1821 | // doTmuxCmd("split-window -hp 50 -d -t '%s:0.0'", Tconsole); | ||
1822 | // doTmuxCmd("split-window -vp 50 -d -t '%s:0.1'", Tconsole); | ||
1823 | // doTmuxCmd("split-window -vp 50 -d -t '%s:0.0'", Tconsole); | ||
1824 | } | ||
1825 | else if (0 == pane) | ||
1826 | { | ||
1827 | doTmuxCmd("new-window -dc '%s/current/bin' -n '%s' -t '%s:%d'", scRoot, type, Tconsole, window); | ||
1828 | if (2 <= panes) | ||
1829 | doTmuxCmd("split-window -hp 50 -d -t '%s:%d.0'", Tconsole, window); | ||
1830 | if (4 <= panes) | ||
1831 | { | ||
1832 | doTmuxCmd("split-window -vp 50 -d -t '%s:%d.1'", Tconsole, window); | ||
1833 | doTmuxCmd("split-window -vp 50 -d -t '%s:%d.0'", Tconsole, window); | ||
1834 | } | ||
1835 | if (8 <= panes) | ||
1836 | { | ||
1837 | doTmuxCmd("split-window -hp 50 -d -t '%s:%d.3'", Tconsole, window); | ||
1838 | doTmuxCmd("split-window -hp 50 -d -t '%s:%d.2'", Tconsole, window); | ||
1839 | doTmuxCmd("split-window -hp 50 -d -t '%s:%d.1'", Tconsole, window); | ||
1840 | doTmuxCmd("split-window -hp 50 -d -t '%s:%d.0'", Tconsole, window); | ||
1841 | } | ||
1842 | } | ||
1843 | |||
1844 | // Create the temporary .ini files. | ||
1845 | shellMe("echo 'IDs={' >%s/IDs_%d.lua", scTemp, window); | ||
1846 | doTmuxCmd("list-panes -t %d -F \"['%d #{pane_index}'] = '#{window_id}.#{pane_id}',\" >> %s/IDs_%d.lua", window, window, scTemp, window); | ||
1847 | shellMe("echo '}\nreturn IDs' >>%s/IDs_%d.lua", scTemp, window); | ||
1848 | |||
1849 | findWindow(simd, sim, type, count, window, panes, pane); | ||
1850 | simd->portH = 8004 + count * 2; | ||
1851 | simd->portU = 8005 + count * 2; | ||
1852 | cmd = xmprintf("rm -fr %s/sim%d; mkdir -p %s/sim%d; sed -E" | ||
1853 | " -e 's@\\[Region\\]@" | ||
1854 | " paneID = \"\\%s\"\\n" | ||
1855 | "\\[Startup\\]\\n" | ||
1856 | " PIDFile = \"\\$\\{Paths\\|PIDPath\\}\\/\\$\\{Const\\|mysim\\}\\.pid\"\\n" | ||
1857 | " LogFile = \"\\$\\{Paths\\|LogPath\\}\\/OpenSim_\\$\\{Const\\|mysim\\}\\.log\"\\n" | ||
1858 | " StatsLogFile = \"\\$\\{Paths\\|LogPath\\}\\/OpenSimStats_\\$\\{Const\\|mysim\\}\\.log\"\\n" | ||
1859 | " ConsoleHistoryFile = \"\\$\\{Paths\\|LogPath\\}\\/OpenSimConsoleHistory_\\$\\{Const\\|mysim\\}\\.txt\"\\n" | ||
1860 | "\\n\\[Region\\]@'" | ||
1861 | " -e 's/InternalPort[[:space:]]*=[[:space:]]*[[:digit:]]+/InternalPort = %d/'" | ||
1862 | " -e 's/InternalPort[[:space:]]*=[[:space:]]*\"[[:digit:]]+\"/InternalPort = %d/'" | ||
1863 | " -e 's/http_listener_port[[:space:]]*=[[:space:]]*[[:digit:]]+/http_listener_port = %d/'" | ||
1864 | " -e 's/http_listener_port[[:space:]]*=[[:space:]]*\"[[:digit:]]+\"/http_listener_port = %d/'" | ||
1865 | " %s >%s", | ||
1866 | scTemp, count, | ||
1867 | scTemp, count, | ||
1868 | simd->paneID, | ||
1869 | simd->portU, | ||
1870 | simd->portU, | ||
1871 | simd->portH, | ||
1872 | simd->portH, | ||
1873 | path, newPath); | ||
1874 | D("Writing .ini file %s with ports %d %d", newPath, simd->portH, simd->portU); | ||
1875 | if (shellMeFail(cmd)) | ||
1876 | E("sed command failed!"); | ||
1877 | free(cmd); | ||
1878 | free(newPath); | ||
1879 | free(path); | ||
1880 | } | ||
1881 | |||
1882 | // Figure out where the sims are running. | ||
1883 | void findSimsTmux(simData *simd, char *sim, char *type, int count, int window, int panes, int pane) | ||
1884 | { | ||
1885 | simd->window = window; | ||
1886 | simd->pane = pane; | ||
1887 | snprintf(toybuf, sizeof(toybuf), "%s/IDs_%d.lua", scTemp, window); | ||
1888 | qtreetbl_t *IDs = Lua2tree(toybuf, "IDs"); | ||
1889 | snprintf(toybuf, sizeof(toybuf), "%d %d", window, pane); | ||
1890 | simd->paneID = xmprintf("%s", qLuaGet(IDs, toybuf)->v.s); | ||
1891 | freeLuaTree(IDs); | ||
1892 | } | ||
1893 | |||
1894 | void startSim(simData *simd, char *sim, char *type, int count, int window, int panes, int pane) | ||
1895 | { | ||
1896 | if (!checkSimIsRunning(simd->tab)) | ||
1897 | { | ||
1898 | I("Tmux tab [%d:%s](pane %d) tmux ID %s, from %s/sim%d - %s is starting.", window, type, pane, simd->paneID, scTemp, count, simd->name); | ||
1899 | doTmuxCmd("select-pane -t %s:%s -T '%s'", Tconsole, simd->paneID, simd->tab); | ||
1900 | snprintf(toybuf, sizeof(toybuf), "mono OpenSim.exe -inidirectory=%s/sim%d", scTemp, count); | ||
1901 | sendTmuxCmd(simd->paneID, toybuf); | ||
1902 | if (0 == ourSims->doIt) | ||
1903 | { | ||
1904 | snprintf(toybuf, sizeof(toybuf), "INITIALIZATION COMPLETE FOR %s", simd->name); | ||
1905 | waitTmuxText(simd->paneID, toybuf); | ||
1906 | I("%s is done starting up.", simd->name); | ||
1907 | sendTmuxCmd(simd->paneID, ""); | ||
1908 | sendTmuxCmd(simd->paneID, ""); | ||
1909 | ourSims->la = waitLoadAverage(ourSims->la, loadAverageInc, simTimeOut); | ||
1910 | if (1 < bulkSims) | ||
1911 | bulkSims = bulkSims / 2; | ||
1912 | } | ||
1913 | else | ||
1914 | { | ||
1915 | // TODO - some compromise, do a premptive load check if we are not doing a wait anyway. | ||
1916 | } | ||
1917 | ourSims->doIt = ((count + 1) % bulkSims); | ||
1918 | } | ||
1919 | } | ||
1920 | |||
1921 | void stopSim(simData *simd, char *sim, char *type, int count, int window, int panes, int pane) | ||
1922 | { | ||
1923 | if (checkSimIsRunning(simd->tab)) | ||
1924 | { | ||
1925 | // Leave empty panes as is. | ||
1926 | V("Attempting to shut down sim %s.", simd->tab); | ||
1927 | sendTmuxCmd(simd->paneID, "show users"); | ||
1928 | sleep(1); | ||
1929 | if (shellMeFail("%s %s/%s capture-pane -Jt '%s' -p | grep -E '^Agents connected: [[:digit:]]*[[:space:]]*$' | tail -n 1 | cut -d ' ' -f 3 >%s/STOP_%s", | ||
1930 | Tcmd, scRun, Tsocket, simd->paneID, scTemp, simd->tab)) | ||
1931 | E("Problem with shutting down sim %s - Failed find 'Agents connected'", simd->tab); | ||
1932 | else | ||
1933 | { | ||
1934 | char *cmd = xmprintf("%s/STOP_%s", scTemp, simd->tab); | ||
1935 | char *r = (char *) qfile_load(cmd, NULL); | ||
1936 | if (r) | ||
1937 | { | ||
1938 | if ('0' == r[0]) | ||
1939 | sendTmuxCmd(simd->paneID, "quit"); | ||
1940 | else | ||
1941 | { | ||
1942 | r[strlen(r) - 1] = '\0'; | ||
1943 | simd->users = atoi(r); | ||
1944 | sendTmuxCmd(simd->paneID, "alert Shutting down this sim, please leave."); | ||
1945 | sendTmuxCmd(simd->paneID, "region restart bluebox \"Shutting down this sim, please leave.\" 300"); | ||
1946 | sendTmuxCmd(simd->paneID, "region restart notice \"Shutting down this sim, please leave.\" 300"); | ||
1947 | sendTmuxCmd(simd->paneID, "region restart abort \"Shutting down this sim, please leave.\""); | ||
1948 | W("Not shutting down %s coz %d people are still on it. They have been warned.", simd->tab, simd->users); | ||
1949 | } | ||
1950 | free(r); | ||
1951 | } | ||
1952 | else | ||
1953 | E("Problem with shutting down sim %s - Failed to read file %s", simd->tab, cmd); | ||
1954 | free(cmd); | ||
1955 | } | ||
1956 | |||
1957 | // There's three things that might happen - | ||
1958 | // Sim will quit, tmux pane will go away before we can see the shutdown message. | ||
1959 | // pane-exited hook might be useful here. | ||
1960 | // Sim will quit, tmux pane wont go away. Dunno yet if waitTmuxText() will still work. | ||
1961 | // pane-died hook might be useful here. | ||
1962 | // Sim fails to quit, error message on the pane that we want to read, wont see the shutdown message. | ||
1963 | // Also sim might not have finished starting up, and may even not be accepting comands yet. | ||
1964 | // TODO - cater for and test them all. | ||
1965 | // Could also loop on checkSimIsRunning(sim) | ||
1966 | // snprintf(toybuf, sizeof(toybuf), "[SHUTDOWN]: Shutdown processing on main thread complete. Exiting..."); | ||
1967 | // waitTmuxText(nm, toybuf); | ||
1968 | // I("%s is done stopping.", simd->name); | ||
1969 | // la = waitLoadAverage(la, loadAverageInc, simTimeOut); | ||
1970 | } | ||
1971 | } | ||
1972 | |||
1973 | typedef struct _ARList ARList; | ||
1974 | struct _ARList | ||
1975 | { | ||
1976 | int len, num; | ||
1977 | char **ARs, *this; | ||
1978 | }; | ||
1979 | |||
1980 | static int filterARs(struct dirtree *node) | ||
1981 | { | ||
1982 | if (!node->parent) return DIRTREE_RECURSE | DIRTREE_SHUTUP; | ||
1983 | ARList *list = (ARList *) node->parent->extra; | ||
1984 | int l0 = strlen(node->name), l1 = strlen(list->this); | ||
1985 | |||
1986 | // TODO - ignore zero sized ones. | ||
1987 | if ((4 < l0) && ((strncmp(&(node->name[l0 - 4]), ".iar", 4) == 0) || (strncmp(&(node->name[l0 - 4]), ".oar", 4) == 0)) && (strncmp(node->name, list->this, l1) == 0)) | ||
1988 | { | ||
1989 | if ((list->num + 1) > list->len) | ||
1990 | { | ||
1991 | list->len = list->len + 1; | ||
1992 | list->ARs = xrealloc(list->ARs, list->len * sizeof(char *)); | ||
1993 | } | ||
1994 | list->ARs[list->num] = xstrdup(node->name); | ||
1995 | list->num++; | ||
1996 | } | ||
1997 | return 0; | ||
1998 | } | ||
1999 | |||
2000 | // Forward declare this. | ||
2001 | my_ulonglong dbCount(char *table, char *where); | ||
2002 | void doSimsThing(simData *simd, char *sim, char *type, int count, int window, int panes, int pane) | ||
2003 | { | ||
2004 | // Check if only doing a single sim. | ||
2005 | int cont = FALSE, member = false; | ||
2006 | char *last = strchr(sim, ' '); | ||
2007 | |||
2008 | if (FLAG(m)) | ||
2009 | { | ||
2010 | // check if it's a real user. | ||
2011 | last[0] = '\0'; last++; | ||
2012 | // Double check it's a real member. | ||
2013 | snprintf(toybuf, sizeof(toybuf), "FirstName='%s' and LastName='%s'", sim, last); | ||
2014 | if (1 == dbCount("UserAccounts", toybuf)) | ||
2015 | member = TRUE; | ||
2016 | else | ||
2017 | E("Can't find member %s %s.", sim, last); | ||
2018 | } | ||
2019 | else if (NULL != ourSims->target) | ||
2020 | { | ||
2021 | /* TODO - maybe? | ||
2022 | byTab has the short name as the key, simData as the value. | ||
2023 | Iterate through it looking for target sims if specified in the sledjchisl arguments. | ||
2024 | Which can be short name, long name, foo.shini file name. | ||
2025 | Though short name is a direct lookup, and "foo.shini" we can strip off the .shini and direct lookup the short name. | ||
2026 | So only need to iterate if it's a long name. | ||
2027 | Keep in mind the argument might be a user name for IAR backups. Or perhaps a UUID. | ||
2028 | */ | ||
2029 | cont = TRUE; | ||
2030 | snprintf(toybuf, sizeof(toybuf), "%s.shini", simd->tab); | ||
2031 | if | ||
2032 | ( | ||
2033 | (strcmp(ourSims->target, simd->name) == 0) || | ||
2034 | (strcmp(ourSims->target, simd->tab) == 0) || | ||
2035 | (strcmp(ourSims->target, toybuf) == 0) | ||
2036 | ) | ||
2037 | cont = FALSE; | ||
2038 | snprintf(toybuf, sizeof(toybuf), "%s.ini", simd->tab); | ||
2039 | if (strcmp(ourSims->target, toybuf) == 0) | ||
2040 | cont = FALSE; | ||
2041 | snprintf(toybuf, sizeof(toybuf), "sim%d.ini", count); | ||
2042 | if (strcmp(ourSims->target, toybuf) == 0) | ||
2043 | cont = FALSE; | ||
2044 | } | ||
2045 | |||
2046 | if (cont) | ||
2047 | return; | ||
2048 | |||
2049 | switch (currentMode) | ||
2050 | { | ||
2051 | case START : // "start 'Welcome sim'" "start Welcome.shini" "Welcome.shini" "start Welcome.ini" "start Welcome" "start" start everything | ||
2052 | { | ||
2053 | startSim(simd, sim, type, count, window, panes, pane); | ||
2054 | break; | ||
2055 | } | ||
2056 | |||
2057 | case BACKUP : // "backup -m 'onefang rejected'" "backup 'Welcome sim'" "backup Welcome.shini" "Welcome.shini backup" "backup Welcome.ini" "backup Welcome" "backup" backup everything | ||
2058 | { | ||
2059 | struct timeval tv; | ||
2060 | time_t curtime; | ||
2061 | char date[DATE_TIME_LEN]; | ||
2062 | |||
2063 | gettimeofday(&tv, NULL); | ||
2064 | curtime = tv.tv_sec; | ||
2065 | strftime(date, DATE_TIME_LEN, "%F_%T", localtime(&curtime)); | ||
2066 | if (FLAG(m)) | ||
2067 | { | ||
2068 | if (member) | ||
2069 | { | ||
2070 | I("Member %s %s is being backed up to %s/%s_%s-%s.iar in tmux windo %s.", sim, last, scBackup, sim, last, date, ourSims->backup); | ||
2071 | snprintf(toybuf, sizeof(toybuf), "save iar -c %s %s / password %s/%s_%s-%s.iar", sim, last, scBackup, sim, last, date); | ||
2072 | if (NULL != ourSims->backup) | ||
2073 | { | ||
2074 | if ('\0' == rSync[0]) | ||
2075 | { | ||
2076 | I("Running gitar on %s %s", sim, last); | ||
2077 | shellMeFail("%s/current/bin/sledjchisl gitar -m %s %s %s", scRoot, FLAG(v) ? "-v" : "", sim, last); | ||
2078 | } | ||
2079 | sendTmuxCmd(ourSims->backup, toybuf); | ||
2080 | // if (0 == do) | ||
2081 | { | ||
2082 | snprintf(toybuf, sizeof(toybuf), "Saved archive with [[:digit:]]+ items for %s %s", sim, last); | ||
2083 | waitTmuxText(ourSims->backup, toybuf); | ||
2084 | I("%s %s is done backing up.", sim, last); | ||
2085 | sendTmuxCmd(ourSims->backup, ""); | ||
2086 | sendTmuxCmd(ourSims->backup, ""); | ||
2087 | if ('\0' != rSync[0]) | ||
2088 | { | ||
2089 | if (shellMeFail("time ionice -c3 nice -n 19 rsync -Ha -R --modify-window=2 --partial --port=%d --remove-source-files %s/*.iar %s", | ||
2090 | rSyncPort, scBackup, rSync)) | ||
2091 | E("rsync failed"); | ||
2092 | } | ||
2093 | ourSims->la = waitLoadAverage(ourSims->la, loadAverageInc, simTimeOut); | ||
2094 | } | ||
2095 | } | ||
2096 | else | ||
2097 | E("Can't find backup sim."); | ||
2098 | } | ||
2099 | } | ||
2100 | else if (checkSimIsRunning(simd->tab)) | ||
2101 | { | ||
2102 | // TODO - should collect names of existing backups, both tmux / ini name and proper name. | ||
2103 | // Scan backups directory once before this for loop, add details to sims list. | ||
2104 | // strip off the last bit of file name (YYYY-mm-dd_HH:MM:SS.oar) to get the name | ||
2105 | // keep in mind some old files had munged names like "Tiffanie_s_Paradise-2021-06-23_05:11:38.oar" | ||
2106 | I("Sim %s is being backed up to %s/backups/%s-%s.oar.", simd->name, scRoot, simd->tab, date); | ||
2107 | if ('\0' == rSync[0]) | ||
2108 | { | ||
2109 | I("Running gitar on %s", simd->tab); | ||
2110 | shellMeFail("%s/current/bin/sledjchisl gitar %s %s", scRoot, FLAG(v) ? "-v" : "", simd->tab); | ||
2111 | } | ||
2112 | snprintf(toybuf, sizeof(toybuf), "save oar --all %s/%s-%s.oar", scBackup, simd->tab, date); | ||
2113 | sendTmuxCmd(simd->paneID, toybuf); | ||
2114 | // if (0 == do) | ||
2115 | { | ||
2116 | snprintf(toybuf, sizeof(toybuf), "Finished writing out OAR for %s", simd->name); | ||
2117 | waitTmuxText(simd->paneID, toybuf); | ||
2118 | I("%s is done backing up.", simd->name); | ||
2119 | sendTmuxCmd(simd->paneID, ""); | ||
2120 | sendTmuxCmd(simd->paneID, ""); | ||
2121 | if ('\0' != rSync[0]) | ||
2122 | { | ||
2123 | if (shellMeFail("time ionice -c3 nice -n 19 rsync -Ha -R --modify-window=2 --partial --port=%d --remove-source-files %s/*.oar %s", | ||
2124 | rSyncPort, scBackup, rSync)) | ||
2125 | E("rsync failed"); | ||
2126 | } | ||
2127 | ourSims->la = waitLoadAverage(ourSims->la, loadAverageInc, simTimeOut); | ||
2128 | } | ||
2129 | } | ||
2130 | break; | ||
2131 | } | ||
2132 | |||
2133 | case GITAR : // "gitAR -m avatar name" "gitAR sim name" | ||
2134 | { | ||
2135 | /* Work around OpenSims slow database corruption bug by using git to store all old backups. | ||
2136 | Try to squeeze every last byte out of the tarballs. Seems to cut the total storage size down to one third the size of just the raw I/OAR files. | ||
2137 | Saves even more if there's been no changes. | ||
2138 | On the other hand, these backup files will grow indefinately, the more changes, the faster it grows. I can live with that for more reliable backups that go back further. | ||
2139 | Tries to avoid loosing data if things go wrong. I think the main remaining problem would be running out of space, in which case you have bigger problems to deal with. | ||
2140 | |||
2141 | Strategy - unpack the last one, unpack and commit any old I/OARs, pack up the result, delete it's working directory, THEN run the save i/oar. | ||
2142 | Avoids having to sync with OpenSim finishing the current I/OAR, and as a bonus, an easy to deliver latest I/OAR for people that want it. | ||
2143 | */ | ||
2144 | char *name = xstrdup(sim); | ||
2145 | |||
2146 | if (FLAG(m)) | ||
2147 | { | ||
2148 | if (member) | ||
2149 | { | ||
2150 | free(name); | ||
2151 | name = xmprintf("%s_%s", sim, last); | ||
2152 | } | ||
2153 | else | ||
2154 | { | ||
2155 | free(name); | ||
2156 | return; | ||
2157 | } | ||
2158 | } | ||
2159 | |||
2160 | char type = FLAG(m) ? 'I' : 'O'; | ||
2161 | char *gar = xmprintf("%s-git%cAR", name, type), *gtr = xmprintf("%s/%s.tar.xz", scBackup, gar); | ||
2162 | char *dir = xmprintf("%s/temp_%c_%s", scBackup, type, name); | ||
2163 | char *glog = xmprintf("%s/log.log", scBackup), *gerr = xmprintf("%s/errors", dir); | ||
2164 | // Make sure stuff that's already compressed doesn't get compressed by git. | ||
2165 | // Also tries to protect binaries from mangling. | ||
2166 | char *gab = xmprintf("%s/%s/.gitattributes", dir, gar), *gad = | ||
2167 | "*.bvh -delta -diff -text\n" | ||
2168 | "*.jp2 -delta -diff -text\n" | ||
2169 | "*.jpg -delta -diff -text\n" | ||
2170 | "*.llmesh -delta -diff -text\n" | ||
2171 | "*.ogg -delta -diff -text\n" | ||
2172 | "*.png -delta -diff -text\n" | ||
2173 | "*.r32 -delta -diff -text\n" | ||
2174 | "*.tga -delta -diff -text\n"; | ||
2175 | ARList *ourARs = xzalloc(sizeof(ARList)); | ||
2176 | |||
2177 | if (qfile_exist(dir)) | ||
2178 | { | ||
2179 | if (shellMeFail("echo 'Mess left over from last backup in %d, not gonna run!' >>%s", dir, gerr)) E("Cleaning up the mess!"); | ||
2180 | if (shellMeFail("mv %s/*.%car %s 2>&1 >>%s", dir, tolower(type), scBackup, gerr)) E("Failed cleaning up the mess!"); | ||
2181 | goto gitARend; | ||
2182 | } | ||
2183 | |||
2184 | // Either unpack the old gitAR, or create a new one. | ||
2185 | qfile_mkdir(dir, S_IRWXU | S_IRGRP | S_IXGRP, true); | ||
2186 | if (qfile_exist(gtr)) | ||
2187 | { | ||
2188 | I("Unpacking %s", gtr); | ||
2189 | if (shellMeFail("cd %s; ionice -c3 nice -n 19 tar -xf %s >>%s", dir, gtr, gerr)) E("Failed to unpack %s!", gtr); | ||
2190 | char *t = xmprintf("%s/%s_git%cAR", dir, name, type); | ||
2191 | // Changed from _ to -, but deal with old archives. | ||
2192 | if (qfile_exist(t)) | ||
2193 | { | ||
2194 | if (shellMeFail("mv %s %s/%s", t, dir, gar)) E("Failed to move %s!", t); | ||
2195 | } | ||
2196 | free(t); | ||
2197 | } | ||
2198 | else | ||
2199 | { | ||
2200 | qfile_mkdir(gar, S_IRWXU | S_IRGRP | S_IXGRP, true); | ||
2201 | // git will create gar for us. | ||
2202 | if (shellMeFail("cd %s; git init --quiet %s >>%s", dir, gar, gerr)) E("Failed to git init %s/%s!", dir, gar); | ||
2203 | |||
2204 | // Coz git insists. | ||
2205 | if (shellMeFail("cd %s/%s; git config user.email \"opensim@$(hostname -A | cut -d ' ' -f 1)\"", dir, gar)) E("Failed to git config user.email!"); | ||
2206 | if (shellMeFail("cd %s/%s; git config user.name \"opensim\"", dir, gar)) E("Failed to git config user.name!"); | ||
2207 | |||
2208 | // Coz git insists on having something comitted before it can change the branch name. | ||
2209 | if (-1 == qfile_save(gab, gad, strlen(gad), false)) E("Faild to write %s file!", gad); | ||
2210 | if (shellMeFail("cd %s/%s; git add .gitattributes >>%s", dir, gar, gerr)) E("Failed to git add!"); | ||
2211 | V("Committing initial git."); | ||
2212 | if (shellMeFail("cd %s/%s; git commit -qm \"Initial commit\" >>%s || echo \"ERROR - Could not commit!\" >>%s", | ||
2213 | dir, gar, glog, gerr)) E("Failed to git commit!"); | ||
2214 | if (shellMeFail("cd %s/%s; git branch -m master Domme >>%s", dir, gar, gerr)) E("Failed to git branch -m master Domme!"); | ||
2215 | } | ||
2216 | // Doing these each time, to catch those old ones that didn't have them. | ||
2217 | // Coz otherwise git commit starts a gc run IN THE BACKGROUD, which screws up trying to tarball it. | ||
2218 | if (shellMeFail("cd %s/%s; git config gc.autodetach false", dir, gar)) E("Failed to git config gc.autodetach!"); | ||
2219 | // Don't want it running in the foreground either, coz it dumps on the console, we do a gc --quiet later anyway. | ||
2220 | if (shellMeFail("cd %s/%s; git config gc.auto 0", dir, gar)) E("Failed to git config gc.auto!"); | ||
2221 | |||
2222 | // Git is such a pedantic bitch, let's just fucking ignore any errors it gives due to lack of anything to do. | ||
2223 | // Even worse the OpenSim devs breaking logout tracking gives git plenty of nothing to do. lol | ||
2224 | |||
2225 | // Loop through the .iar / .oar files in backups, ignoring zero sized files. | ||
2226 | struct dirtree *new = dirtree_add_node(0, scBackup, 0); | ||
2227 | int i; | ||
2228 | |||
2229 | ourARs->num = 0; | ||
2230 | ourARs->this = xmprintf("%s-", name); | ||
2231 | new->extra = (long) ourARs; | ||
2232 | dirtree_handle_callback(new, filterARs); | ||
2233 | qsort(ourARs->ARs, ourARs->num, sizeof(char *), qstrcmp); | ||
2234 | for (i = 0; i < ourARs->num; i++) | ||
2235 | { | ||
2236 | I("Adding %s to %s", ourARs->ARs[i], gtr); | ||
2237 | // Deal with deletions in the inventory / sim, easy method, which becomes a nop for files that stay in the git add below. | ||
2238 | if (shellMeFail("cd %s/%s; rm -fr * >>%s", dir, gar, gerr)) E("Failed to rm!"); | ||
2239 | if (shellMeFail("cd %s/%s; ionice -c3 nice -n 19 tar -xzf \"%s/%s\" || echo \"ERROR - Could not unpack %s !\" >>%s", | ||
2240 | dir, gar, scBackup, ourARs->ARs[i], ourARs->ARs[i], gerr)) E("Failed to unpack %s!", ourARs->ARs[i]); | ||
2241 | if (!hasContents(gerr)) | ||
2242 | { | ||
2243 | if (shellMeFail("cd %s/%s; git add * >>%s", dir, gar, glog)) E("Failed to git add!"); | ||
2244 | // The \\* bit is to escape the \ from snprintf, which itself escapes the * from the shell. | ||
2245 | if (shellMeFail("cd %s/%s; git add */\\* >>%s", dir, gar, gerr)) E("Failed to git add!"); | ||
2246 | // Gotta add this again, coz of the rm. Apparently rm removes dotfiles, but add doesn't! | ||
2247 | if (-1 == qfile_save(gab, gad, strlen(gad), false)) E("Faild to write %s file!", gab); | ||
2248 | if (shellMeFail("cd %s/%s; git add .gitattributes >>%s", dir, gar, gerr)) E("Failed to git add!"); | ||
2249 | |||
2250 | // Magic needed to figure out if there's anything to commit. | ||
2251 | // After all the pain to get this to work, there's an ever changing timestamp in archive.xml that screws it up. | ||
2252 | // Like this system didn't have enough timestamps in it already. lol | ||
2253 | // TODO - I could sed out that timestamp, and put it back again based on the OAR file name when extracting. | ||
2254 | // IARs don't seem to have the timestamp. | ||
2255 | int j = shellMe("cd %s/%s; t=$(git status --porcelain) && [ -z \"${t}\" ]", dir, gar); | ||
2256 | if (!WIFEXITED(j)) E("git status failed!"); | ||
2257 | else if (1 == WEXITSTATUS(j)) | ||
2258 | { | ||
2259 | V("Committing changes from %s", ourARs->ARs[i]); | ||
2260 | // Note this commit message has to be just the file name, as the ungitAR script uses it. | ||
2261 | if (shellMeFail("cd %s/%s; git commit -a -qm \"%s\" >>%s || echo \"ERROR - Could not commit %s !\" >>%s ", | ||
2262 | dir, gar, ourARs->ARs[i], ourARs->ARs[i], glog, gerr)) E("Failed to git commit!"); | ||
2263 | if (hasContents(gerr)) | ||
2264 | { | ||
2265 | free(ourARs->ARs[i]); | ||
2266 | goto gitARend; | ||
2267 | } | ||
2268 | } | ||
2269 | else | ||
2270 | V("No changes to commit from %s.", ourARs->ARs[i]); | ||
2271 | } | ||
2272 | if (!hasContents(gerr)) | ||
2273 | { | ||
2274 | if (shellMeFail("mv %s/%s %s", scBackup, ourARs->ARs[i], dir)) E("Failed to move %s!", ourARs->ARs[i]); | ||
2275 | } | ||
2276 | free(ourARs->ARs[i]); | ||
2277 | } | ||
2278 | |||
2279 | if (!hasContents(gerr)) | ||
2280 | { | ||
2281 | I("Checking git repo %s", gtr); | ||
2282 | if (shellMeFail("cd %s/%s; git fsck --strict --lost-found --no-progress >>%s || echo \"ERROR - Problem with git fsck %s !\" >>%s ", | ||
2283 | dir, gar, glog, gtr, gerr)) E("Failed to git fsck!"); | ||
2284 | if (shellMeFail("cd %s/%s; git gc --aggressive --prune=all --quiet >>%s || echo \"ERROR - Problem with git gc %s !\" >>%s ", | ||
2285 | dir, gar, glog, gtr, gerr)) E("Failed to git gc!"); | ||
2286 | I("Compressing %s", gtr); | ||
2287 | if (shellMeFail("cd %s; XZ_OPT='-9e' ionice -c3 nice -n 19 tar -c --xz %s -f %s || echo 'ERROR - Could not pack gitAR!' >>%s", | ||
2288 | dir, gar, gtr, gerr)) E("Failed to git add!"); | ||
2289 | } | ||
2290 | gitARend: | ||
2291 | if (hasContents(gerr)) E("Failed to process the archives, look in %s for errors!", gerr); | ||
2292 | else | ||
2293 | { | ||
2294 | if (shellMeFail("rm -fr %s", dir)) E("Failed to rm!"); | ||
2295 | } | ||
2296 | free(ourARs->ARs); | ||
2297 | free(ourARs->this); | ||
2298 | free(ourARs); | ||
2299 | free(gab); | ||
2300 | free(gerr); | ||
2301 | free(glog); | ||
2302 | free(dir); | ||
2303 | free(gtr); | ||
2304 | free(gar); | ||
2305 | free(name); | ||
2306 | break; | ||
2307 | } | ||
2308 | |||
2309 | case RESTART : // "restart 'Welcome sim'" "restart Welcome.shini" "Welcome.shini restart" "restart Welcome.ini" "restart Welcome" "restart" restart everything | ||
2310 | { | ||
2311 | I("Tmux tab [%d:%s](pane %d) tmux ID %s, from %s/sim%d - %s is stopping.", window, type, pane, simd->paneID, scTemp, count, simd->name); | ||
2312 | stopSim(simd, sim, type, count, window, panes, pane); | ||
2313 | while (checkSimIsRunning(simd->tab)) | ||
2314 | usleep(100000); | ||
2315 | usleep(100000); | ||
2316 | startSim(simd, sim, type, count, window, panes, pane); | ||
2317 | break; | ||
2318 | } | ||
2319 | |||
2320 | case STATUS : // "status 'Welcome sim'" "status Welcome.shini" "Welcome.shini status" "status Welcome.ini" "status Welcome" "status" status everything | ||
2321 | { | ||
2322 | // TODO - report CPU and memory used as well. | ||
2323 | if (FLAG(m)) | ||
2324 | { | ||
2325 | ; // Do nothing, this is better done in the calling function, coz it has the details. | ||
2326 | } | ||
2327 | else if (checkSimIsRunning(simd->tab)) | ||
2328 | I("%s is running in Tmux tab [%d:%s](pane %d) tmux ID %s, from %s/sim%d", simd->name, window, type, pane, simd->paneID, scTemp, count); | ||
2329 | else | ||
2330 | W("%s is not running.", simd->name); | ||
2331 | break; | ||
2332 | } | ||
2333 | |||
2334 | case STOP : // "stop 'Welcome sim'" "stop Welcome.shini" "Welcome.shini stop" "stop Welcome.ini" "stop Welcome" "stop" stop everything | ||
2335 | { | ||
2336 | stopSim(simd, sim, type, count, window, panes, pane); | ||
2337 | break; | ||
2338 | } | ||
2339 | |||
2340 | default : | ||
2341 | { | ||
2342 | E("Unknown command! %s", toys.optargs[0]); | ||
2343 | break; | ||
2344 | } | ||
2345 | } | ||
2346 | } | ||
2347 | |||
2348 | void stopSims(simData *simd, char *sim, char *type, int count, int window, int panes, int pane) | ||
2349 | { | ||
2350 | // TODO - should move onto others if this one takes too long, and come back later. | ||
2351 | // NOTE - these sleeps are guesses, seems to work on my super desktop during testing. | ||
2352 | if (checkSimIsRunning(simd->tab)) | ||
2353 | T("Tmux tab [%d:%s](pane %d) tmux ID %s, from %s/sim%d - %s has NOT stopped YET.", window, type, pane, simd->paneID, scTemp, count, simd->name); | ||
2354 | if (simd->users) | ||
2355 | { | ||
2356 | ourSims->doIt = 0; | ||
2357 | W("Not shutting down %s coz %d people are still on it. They have been warned.", simd->tab, simd->users); | ||
2358 | } | ||
2359 | else | ||
2360 | { | ||
2361 | while (checkSimIsRunning(simd->tab)) | ||
2362 | usleep(100000); | ||
2363 | usleep(10000); | ||
2364 | doTmuxCmd("kill-pane -t %s", simd->paneID); | ||
2365 | I("Tmux tab [%d:%s](pane %d) tmux ID %s, from %s/sim%d - %s has stopped.", window, type, pane, simd->paneID, scTemp, count, simd->name); | ||
2366 | } | ||
2367 | } | ||
2368 | |||
841 | 2369 | ||
842 | static void PrintEnv(qgrow_t *reply, char *label, char **envp) | 2370 | static void PrintEnv(qgrow_t *reply, char *label, char **envp) |
843 | { | 2371 | { |
@@ -1128,7 +2656,6 @@ static boolean dbCheckError(char *error, char *sql) | |||
1128 | if (2006 == e) | 2656 | if (2006 == e) |
1129 | { | 2657 | { |
1130 | W("Reconnecting to database."); | 2658 | W("Reconnecting to database."); |
1131 | // TODO - still failing to reconnect for dbCount() on coffeegrid. | ||
1132 | freeDb(false); | 2659 | freeDb(false); |
1133 | return dbConnect(); | 2660 | return dbConnect(); |
1134 | } | 2661 | } |
@@ -1257,6 +2784,8 @@ int dbDoSomething(dbRequest *req, boolean count, ...) | |||
1257 | if (-1 == clock_gettime(CLOCK_REALTIME, &then)) | 2784 | if (-1 == clock_gettime(CLOCK_REALTIME, &then)) |
1258 | perror_msg("Unable to get the time."); | 2785 | perror_msg("Unable to get the time."); |
1259 | 2786 | ||
2787 | // TODO - should factor this out to it's own function, and call that function in dbCount() and dbCountJoin(). | ||
2788 | // Or better yet, finally migrate those functions to using dbDoSomething(). | ||
1260 | double n = (dbLast.tv_sec * 1000000000.0) + dbLast.tv_nsec; | 2789 | double n = (dbLast.tv_sec * 1000000000.0) + dbLast.tv_nsec; |
1261 | double t = (then.tv_sec * 1000000000.0) + then.tv_nsec; | 2790 | double t = (then.tv_sec * 1000000000.0) + then.tv_nsec; |
1262 | 2791 | ||
@@ -1701,7 +3230,7 @@ I("count!!!!!!!!!!!!!!!!"); | |||
1701 | signed char d = (signed char) c; | 3230 | signed char d = (signed char) c; |
1702 | 3231 | ||
1703 | memcpy(req->inBind[i].buffer, &d, (size_t) fld->length); | 3232 | memcpy(req->inBind[i].buffer, &d, (size_t) fld->length); |
1704 | //T("TINY %d %s %d", i, fld->name, req->inBind[i].buffer_length); | 3233 | //V("TINY %d %s %d", i, fld->name, req->inBind[i].buffer_length); |
1705 | break; | 3234 | break; |
1706 | } | 3235 | } |
1707 | 3236 | ||
@@ -1711,7 +3240,7 @@ I("count!!!!!!!!!!!!!!!!"); | |||
1711 | short int d = (short int) c; | 3240 | short int d = (short int) c; |
1712 | 3241 | ||
1713 | memcpy(req->inBind[i].buffer, &d, (size_t) fld->length); | 3242 | memcpy(req->inBind[i].buffer, &d, (size_t) fld->length); |
1714 | //T("SHORT %d %s %d = %d", i, fld->name, req->inBind[i].buffer_length, c); | 3243 | //V("SHORT %d %s %d = %d", i, fld->name, req->inBind[i].buffer_length, c); |
1715 | break; | 3244 | break; |
1716 | } | 3245 | } |
1717 | 3246 | ||
@@ -1720,7 +3249,7 @@ I("count!!!!!!!!!!!!!!!!"); | |||
1720 | int d = va_arg(ap, int); | 3249 | int d = va_arg(ap, int); |
1721 | 3250 | ||
1722 | memcpy(req->inBind[i].buffer, &d, (size_t) fld->length); | 3251 | memcpy(req->inBind[i].buffer, &d, (size_t) fld->length); |
1723 | //T("INT24 %d %s %d - %d", i, fld->name, req->inBind[i].buffer_length, d); | 3252 | //V("INT24 %d %s %d - %d", i, fld->name, req->inBind[i].buffer_length, d); |
1724 | break; | 3253 | break; |
1725 | } | 3254 | } |
1726 | 3255 | ||
@@ -1729,7 +3258,7 @@ I("count!!!!!!!!!!!!!!!!"); | |||
1729 | long d = va_arg(ap, long); | 3258 | long d = va_arg(ap, long); |
1730 | 3259 | ||
1731 | memcpy(req->inBind[i].buffer, &d, (size_t) fld->length); | 3260 | memcpy(req->inBind[i].buffer, &d, (size_t) fld->length); |
1732 | //T("LONG %d %s %d = %ld", i, fld->name, req->inBind[i].buffer_length, d); | 3261 | //V("LONG %d %s %d = %ld", i, fld->name, req->inBind[i].buffer_length, d); |
1733 | break; | 3262 | break; |
1734 | } | 3263 | } |
1735 | 3264 | ||
@@ -1738,7 +3267,7 @@ I("count!!!!!!!!!!!!!!!!"); | |||
1738 | long long int d = va_arg(ap, long long int); | 3267 | long long int d = va_arg(ap, long long int); |
1739 | 3268 | ||
1740 | memcpy(req->inBind[i].buffer, &d, (size_t) fld->length); | 3269 | memcpy(req->inBind[i].buffer, &d, (size_t) fld->length); |
1741 | //T("LONGLONG %d %s %d = %lld", i, fld->name, req->inBind[i].buffer_length, d); | 3270 | //V("LONGLONG %d %s %d = %lld", i, fld->name, req->inBind[i].buffer_length, d); |
1742 | break; | 3271 | break; |
1743 | } | 3272 | } |
1744 | 3273 | ||
@@ -1748,7 +3277,7 @@ I("count!!!!!!!!!!!!!!!!"); | |||
1748 | float d = (float) c; | 3277 | float d = (float) c; |
1749 | 3278 | ||
1750 | memcpy(req->inBind[i].buffer, &d, (size_t) fld->length); | 3279 | memcpy(req->inBind[i].buffer, &d, (size_t) fld->length); |
1751 | //T("FLOAT %d %s %d = %f", i, fld->name, req->inBind[i].buffer_length, d); | 3280 | //V("FLOAT %d %s %d = %f", i, fld->name, req->inBind[i].buffer_length, d); |
1752 | break; | 3281 | break; |
1753 | } | 3282 | } |
1754 | 3283 | ||
@@ -1757,13 +3286,13 @@ I("count!!!!!!!!!!!!!!!!"); | |||
1757 | double d = va_arg(ap, double); | 3286 | double d = va_arg(ap, double); |
1758 | 3287 | ||
1759 | memcpy(req->inBind[i].buffer, &d, (size_t) fld->length); | 3288 | memcpy(req->inBind[i].buffer, &d, (size_t) fld->length); |
1760 | //T("DOUBLE %d %s %d = %f", i, fld->name, req->inBind[i].buffer_length, d); | 3289 | //V("DOUBLE %d %s %d = %f", i, fld->name, req->inBind[i].buffer_length, d); |
1761 | break; | 3290 | break; |
1762 | } | 3291 | } |
1763 | 3292 | ||
1764 | case MYSQL_TYPE_NEWDECIMAL: | 3293 | case MYSQL_TYPE_NEWDECIMAL: |
1765 | { | 3294 | { |
1766 | //T("NEWDECIMAL %d %s %d", i, fld->name, req->inBind[i].buffer_length); | 3295 | //V("NEWDECIMAL %d %s %d", i, fld->name, req->inBind[i].buffer_length); |
1767 | break; | 3296 | break; |
1768 | } | 3297 | } |
1769 | 3298 | ||
@@ -1775,7 +3304,7 @@ I("count!!!!!!!!!!!!!!!!"); | |||
1775 | MYSQL_TIME d = va_arg(ap, MYSQL_TIME); | 3304 | MYSQL_TIME d = va_arg(ap, MYSQL_TIME); |
1776 | 3305 | ||
1777 | memcpy(req->inBind[i].buffer, &d, (size_t) fld->length); | 3306 | memcpy(req->inBind[i].buffer, &d, (size_t) fld->length); |
1778 | //T("DATE / TIME ish %d %s %d", i, fld->name, req->inBind[i].buffer_length); | 3307 | //V("DATE / TIME ish %d %s %d", i, fld->name, req->inBind[i].buffer_length); |
1779 | break; | 3308 | break; |
1780 | } | 3309 | } |
1781 | 3310 | ||
@@ -1790,7 +3319,7 @@ I("count!!!!!!!!!!!!!!!!"); | |||
1790 | *(req->inBind[i].length) = l; | 3319 | *(req->inBind[i].length) = l; |
1791 | strncpy(req->inBind[i].buffer, d, (size_t) l); | 3320 | strncpy(req->inBind[i].buffer, d, (size_t) l); |
1792 | ((char *) req->inBind[i].buffer)[l] = '\0'; | 3321 | ((char *) req->inBind[i].buffer)[l] = '\0'; |
1793 | //T("STRING / VARSTRING %d %s %d = %s", i, fld->name, req->inBind[i].buffer_length, d); | 3322 | //V("STRING / VARSTRING %d %s %d = %s", i, fld->name, req->inBind[i].buffer_length, d); |
1794 | break; | 3323 | break; |
1795 | } | 3324 | } |
1796 | 3325 | ||
@@ -1800,19 +3329,19 @@ I("count!!!!!!!!!!!!!!!!"); | |||
1800 | case MYSQL_TYPE_LONG_BLOB: | 3329 | case MYSQL_TYPE_LONG_BLOB: |
1801 | { | 3330 | { |
1802 | // TODO - should write this, we will likely need it. Main problem is - how long is this blob? Probably should add a length param before the blob. | 3331 | // TODO - should write this, we will likely need it. Main problem is - how long is this blob? Probably should add a length param before the blob. |
1803 | //T("BLOBs %d %s %d", i, fld->name, req->inBind[i].buffer_length); | 3332 | //V("BLOBs %d %s %d", i, fld->name, req->inBind[i].buffer_length); |
1804 | break; | 3333 | break; |
1805 | } | 3334 | } |
1806 | 3335 | ||
1807 | case MYSQL_TYPE_BIT: | 3336 | case MYSQL_TYPE_BIT: |
1808 | { | 3337 | { |
1809 | //T("BIT %d %s %d", i, fld->name, req->inBind[i].buffer_length); | 3338 | //V("BIT %d %s %d", i, fld->name, req->inBind[i].buffer_length); |
1810 | break; | 3339 | break; |
1811 | } | 3340 | } |
1812 | 3341 | ||
1813 | case MYSQL_TYPE_NULL: | 3342 | case MYSQL_TYPE_NULL: |
1814 | { | 3343 | { |
1815 | //T("NULL %d %s %d", i, fld->name, req->inBind[i].buffer_length); | 3344 | //V("NULL %d %s %d", i, fld->name, req->inBind[i].buffer_length); |
1816 | break; | 3345 | break; |
1817 | } | 3346 | } |
1818 | } | 3347 | } |
@@ -1988,7 +3517,7 @@ end: | |||
1988 | if (-1 == clock_gettime(CLOCK_REALTIME, &dbLast)) | 3517 | if (-1 == clock_gettime(CLOCK_REALTIME, &dbLast)) |
1989 | perror_msg("Unable to get the time."); | 3518 | perror_msg("Unable to get the time."); |
1990 | n = (dbLast.tv_sec * 1000000000.0) + dbLast.tv_nsec; | 3519 | n = (dbLast.tv_sec * 1000000000.0) + dbLast.tv_nsec; |
1991 | T("dbDoSomething(%s) took %lf seconds", req->sql, (n - t) / 1000000000.0); | 3520 | t("dbDoSomething(%s) took %lf seconds", req->sql, (n - t) / 1000000000.0); |
1992 | 3521 | ||
1993 | return ret; | 3522 | return ret; |
1994 | } | 3523 | } |
@@ -2064,7 +3593,7 @@ my_ulonglong dbCount(char *table, char *where) | |||
2064 | perror_msg("Unable to get the time."); | 3593 | perror_msg("Unable to get the time."); |
2065 | double n = (now.tv_sec * 1000000000.0) + now.tv_nsec; | 3594 | double n = (now.tv_sec * 1000000000.0) + now.tv_nsec; |
2066 | double t = (then.tv_sec * 1000000000.0) + then.tv_nsec; | 3595 | double t = (then.tv_sec * 1000000000.0) + then.tv_nsec; |
2067 | // T("dbCount(%s) took %lf seconds", sql, (n - t) / 1000000000.0); | 3596 | // t("dbCount(%s) took %lf seconds", sql, (n - t) / 1000000000.0); |
2068 | free(sql); | 3597 | free(sql); |
2069 | return ret; | 3598 | return ret; |
2070 | } | 3599 | } |
@@ -2113,7 +3642,7 @@ my_ulonglong dbCountJoin(char *table, char *select, char *join, char *where) | |||
2113 | perror_msg("Unable to get the time."); | 3642 | perror_msg("Unable to get the time."); |
2114 | double n = (now.tv_sec * 1000000000.0) + now.tv_nsec; | 3643 | double n = (now.tv_sec * 1000000000.0) + now.tv_nsec; |
2115 | double t = (then.tv_sec * 1000000000.0) + then.tv_nsec; | 3644 | double t = (then.tv_sec * 1000000000.0) + then.tv_nsec; |
2116 | // T("dbCointJoin(%s) took %lf seconds", sql, (n - t) / 1000000000.0); | 3645 | // t("dbCointJoin(%s) took %lf seconds", sql, (n - t) / 1000000000.0); |
2117 | free(sql); | 3646 | free(sql); |
2118 | return ret; | 3647 | return ret; |
2119 | } | 3648 | } |
@@ -2181,7 +3710,7 @@ gridStats *getStats(MYSQL *db, gridStats *stats) | |||
2181 | return stats; | 3710 | return stats; |
2182 | } | 3711 | } |
2183 | 3712 | ||
2184 | I("Getting fresh grid stats."); | 3713 | V("Getting fresh grid stats."); |
2185 | if (checkSimIsRunning("ROBUST")) | 3714 | if (checkSimIsRunning("ROBUST")) |
2186 | replaceStr(stats->stats, "gridOnline", "online"); | 3715 | replaceStr(stats->stats, "gridOnline", "online"); |
2187 | else | 3716 | else |
@@ -2418,6 +3947,44 @@ void outizeCookie(qgrow_t *reply, qhashtbl_t *tbl, char *label) | |||
2418 | reply->addstr(reply, "</pre>\n"); | 3947 | reply->addstr(reply, "</pre>\n"); |
2419 | } | 3948 | } |
2420 | 3949 | ||
3950 | void list2cookie(reqData *Rd, char *cki, qlist_t *list) | ||
3951 | { | ||
3952 | char *t0 = xstrdup(""); | ||
3953 | qlist_obj_t obj; | ||
3954 | |||
3955 | memset((void*)&obj, 0, sizeof(obj)); // must be cleared before call | ||
3956 | list->lock(list); | ||
3957 | while (list->getnext(list, &obj, false) == true) | ||
3958 | { | ||
3959 | char *t1 = xmprintf("%s\n%s", t0, (char *) obj.data); | ||
3960 | |||
3961 | free(t0); | ||
3962 | t0 = t1; | ||
3963 | } | ||
3964 | list->unlock(list); | ||
3965 | setCookie(Rd, cki, &t0[1]); // Skip the first empty one. | ||
3966 | // TODO - should set a very short maxAge. | ||
3967 | free(t0); | ||
3968 | } | ||
3969 | |||
3970 | qlist_t *cookie2list(qhashtbl_t *cookies, char *cki) | ||
3971 | { | ||
3972 | qlist_t *ret = NULL; | ||
3973 | cookie *ck = (cookie *) cookies->get(cookies, cki, NULL, false); | ||
3974 | |||
3975 | if (NULL != ck) | ||
3976 | { | ||
3977 | if (NULL != ck->value) | ||
3978 | { | ||
3979 | qurl_decode(ck->value); | ||
3980 | ret = qstrtokenizer(ck->value, "\n"); | ||
3981 | free(ck->value); | ||
3982 | } | ||
3983 | // TODO - should send the "delete this cookie" thing to the browser. | ||
3984 | cookies->remove(cookies, cki); | ||
3985 | } | ||
3986 | return ret; | ||
3987 | } | ||
2421 | 3988 | ||
2422 | 3989 | ||
2423 | enum fragmentType | 3990 | enum fragmentType |
@@ -3216,28 +4783,6 @@ https://stackoverflow.com/questions/16891729/best-practices-salting-peppering-pa | |||
3216 | qlisttbl_t *accountLevels = NULL; | 4783 | qlisttbl_t *accountLevels = NULL; |
3217 | 4784 | ||
3218 | 4785 | ||
3219 | static void bitch(reqData *Rd, char *message, char *log) | ||
3220 | { | ||
3221 | addStrL(Rd->errors, message); | ||
3222 | E("%s %s %s - %s %s", getStrH(Rd->headers, "REMOTE_ADDR"), Rd->shs.UUID, Rd->shs.name, message, log); | ||
3223 | } | ||
3224 | |||
3225 | /* "A token cookie that references a non-existent session, its value should be replaced immediately to prevent session fixation." | ||
3226 | https://owasp.org/www-community/attacks/Session_fixation | ||
3227 | Which describes the problem, but offers no solution. | ||
3228 | See https://stackoverflow.com/questions/549/the-definitive-guide-to-form-based-website-authentication?rq=1. | ||
3229 | I think this means send a new cookie. | ||
3230 | I clear out the cookies and send blank ones with -1 maxAge, so they should get deleted. | ||
3231 | */ | ||
3232 | static void bitchSession(reqData *Rd, char *message, char *log) | ||
3233 | { | ||
3234 | if ('\0' != message[0]) | ||
3235 | addStrL(Rd->errors, message); | ||
3236 | C("%s %s %s - %s %s", getStrH(Rd->headers, "REMOTE_ADDR"), Rd->shs.UUID, Rd->shs.name, message, log); | ||
3237 | Rd->shs.status = SHS_BOGUS; | ||
3238 | } | ||
3239 | |||
3240 | |||
3241 | // The ancient, insecure since 2011, Second Life / OpenSim password hashing algorithm. | 4786 | // The ancient, insecure since 2011, Second Life / OpenSim password hashing algorithm. |
3242 | char *newSLOSsalt(reqData *Rd) | 4787 | char *newSLOSsalt(reqData *Rd) |
3243 | { | 4788 | { |
@@ -3263,7 +4808,7 @@ char *checkSLOSpassword(reqData *Rd, char *salt, char *password, char *passwordH | |||
3263 | unsigned char *md5hash = xzalloc(17); | 4808 | unsigned char *md5hash = xzalloc(17); |
3264 | char *hash = NULL, *passHash = NULL; | 4809 | char *hash = NULL, *passHash = NULL; |
3265 | 4810 | ||
3266 | T("checkSLOSpassword(%s, %s, %s, ", password, salt, passwordHash, fail); | 4811 | t("checkSLOSpassword(%s, %s, %s, ", password, salt, passwordHash, fail); |
3267 | // Calculate passHash. | 4812 | // Calculate passHash. |
3268 | if (!qhashmd5((void *) password, strlen(password), md5hash)) | 4813 | if (!qhashmd5((void *) password, strlen(password), md5hash)) |
3269 | { | 4814 | { |
@@ -3302,72 +4847,6 @@ T("checkSLOSpassword(%s, %s, %s, ", password, salt, passwordHash, fail); | |||
3302 | } | 4847 | } |
3303 | 4848 | ||
3304 | 4849 | ||
3305 | int LuaToHash(reqData *Rd, char *file, char *var, qhashtbl_t *tnm, int ret, struct stat *st, struct timespec *now, char *type) | ||
3306 | { | ||
3307 | struct timespec then; | ||
3308 | |||
3309 | if (-1 == clock_gettime(CLOCK_REALTIME, &then)) | ||
3310 | perror_msg("Unable to get the time."); | ||
3311 | I("Reading %s file %s", type, file); | ||
3312 | if (0 != stat(file, st)) | ||
3313 | { | ||
3314 | D("No %s file.", file); | ||
3315 | perror_msg("Unable to stat %s", file); | ||
3316 | ret++; | ||
3317 | } | ||
3318 | else | ||
3319 | { | ||
3320 | int status = luaL_loadfile(Rd->L, file), result; | ||
3321 | |||
3322 | if (status) | ||
3323 | { | ||
3324 | bitch(Rd, "No such thing.", "Can't load file."); | ||
3325 | E("Couldn't load file: %s", lua_tostring(Rd->L, -1)); | ||
3326 | ret++; | ||
3327 | } | ||
3328 | else | ||
3329 | { | ||
3330 | result = lua_pcall(Rd->L, 0, LUA_MULTRET, 0); | ||
3331 | |||
3332 | if (result) | ||
3333 | { | ||
3334 | bitch(Rd, "Broken thing.", "Can't run file."); | ||
3335 | E("Failed to run script: %s", lua_tostring(Rd->L, -1)); | ||
3336 | ret++; | ||
3337 | } | ||
3338 | else | ||
3339 | { | ||
3340 | lua_getglobal(Rd->L, var); | ||
3341 | lua_pushnil(Rd->L); | ||
3342 | |||
3343 | while(lua_next(Rd->L, -2) != 0) | ||
3344 | { | ||
3345 | char *n = (char *) lua_tostring(Rd->L, -2); | ||
3346 | |||
3347 | if (lua_isstring(Rd->L, -1)) | ||
3348 | { | ||
3349 | tnm->putstr(tnm, n, (char *) lua_tostring(Rd->L, -1)); | ||
3350 | d("Lua reading (%s) %s = %s", type, n, getStrH(tnm, n)); | ||
3351 | } | ||
3352 | else | ||
3353 | { | ||
3354 | char *v = (char *) lua_tostring(Rd->L, -1); | ||
3355 | W("Unknown Lua variable type for %s = %s", n, v); | ||
3356 | } | ||
3357 | lua_pop(Rd->L, 1); | ||
3358 | } | ||
3359 | |||
3360 | if (-1 == clock_gettime(CLOCK_REALTIME, now)) | ||
3361 | perror_msg("Unable to get the time."); | ||
3362 | double n = (now->tv_sec * 1000000000.0) + now->tv_nsec; | ||
3363 | double t = (then.tv_sec * 1000000000.0) + then.tv_nsec; | ||
3364 | T("Reading %s file took %lf seconds", type, (n - t) / 1000000000.0); | ||
3365 | } | ||
3366 | } | ||
3367 | } | ||
3368 | |||
3369 | return ret; | ||
3370 | } | ||
3371 | 4850 | ||
3372 | 4851 | ||
3373 | char *checkLinky(reqData *Rd) | 4852 | char *checkLinky(reqData *Rd) |
@@ -3380,8 +4859,19 @@ char *checkLinky(reqData *Rd) | |||
3380 | char *t1 = qurl_encode(t0, strlen(t0)); | 4859 | char *t1 = qurl_encode(t0, strlen(t0)); |
3381 | free(ret); | 4860 | free(ret); |
3382 | ret = xmprintf("<p><font color='red'><b>You have an email waiting with a validation link in it, please check your email. " | 4861 | ret = xmprintf("<p><font color='red'><b>You have an email waiting with a validation link in it, please check your email. " |
4862 | "THE FOLLOWING IS REPEATED, COZ PEOPLE JUST DON'T FOLLOW DIRECTIONS!" | ||
3383 | "It will be from %s@%s, and it might be in your spam folder, coz these sorts of emails sometimes end up there. " | 4863 | "It will be from %s@%s, and it might be in your spam folder, coz these sorts of emails sometimes end up there. " |
3384 | "You should add that email address to your contacts, or otherwise let it through your spam filter. " | 4864 | "You should add that email address to your contacts, or otherwise let it through your spam filter. " |
4865 | "It will be from %s@%s, and it might be in your spam folder, coz these sorts of emails sometimes end up there. " | ||
4866 | "You should add that email address to your contacts, or otherwise let it through your spam filter. " | ||
4867 | "It will be from %s@%s, and it might be in your spam folder, coz these sorts of emails sometimes end up there. " | ||
4868 | "You should add that email address to your contacts, or otherwise let it through your spam filter. " | ||
4869 | "It will be from %s@%s, and it might be in your spam folder, coz these sorts of emails sometimes end up there. " | ||
4870 | "You should add that email address to your contacts, or otherwise let it through your spam filter. " | ||
4871 | "If your email client wont let you click the validation link, just copy and paste it into your web browser. " | ||
4872 | "If your email client wont let you click the validation link, just copy and paste it into your web browser. " | ||
4873 | "If your email client wont let you click the validation link, just copy and paste it into your web browser. " | ||
4874 | "If your email client wont let you click the validation link, just copy and paste it into your web browser. " | ||
3385 | // "<a href='https://%s%s?hashish=%s'>%s</a>" | 4875 | // "<a href='https://%s%s?hashish=%s'>%s</a>" |
3386 | "</b></font></p>\n", | 4876 | "</b></font></p>\n", |
3387 | "grid_no_reply", Rd->Host, | 4877 | "grid_no_reply", Rd->Host, |
@@ -3607,15 +5097,29 @@ t("Write shs %s", tnm4); | |||
3607 | "\n" | 5097 | "\n" |
3608 | "Please go to this web link to validate your new account -\n" | 5098 | "Please go to this web link to validate your new account -\n" |
3609 | "https://%s%s?hashish=%s\n" | 5099 | "https://%s%s?hashish=%s\n" |
5100 | "THE FOLLOWING IS REPEATED, COZ PEOPLE JUST DON'T FOLLOW DIRECTIONS!" | ||
5101 | "If your email client wont let you click the validation\n" | ||
5102 | "link, just copy and paste it into your web browser.\n" | ||
5103 | "If your email client wont let you click the validation\n" | ||
5104 | "link, just copy and paste it into your web browser.\n" | ||
5105 | "If your email client wont let you click the validation\n" | ||
5106 | "link, just copy and paste it into your web browser.\n" | ||
5107 | "If your email client wont let you click the validation\n" | ||
5108 | "link, just copy and paste it into your web browser.\n" | ||
5109 | "\n" | ||
3610 | "\n" | 5110 | "\n" |
3611 | "Do not reply to this email.\n" | 5111 | "Do not reply to this email.\n" |
3612 | "\n", | 5112 | "\n" |
5113 | "\n" | ||
5114 | "A copy of the grids Terms of Service that you agreed to\n" | ||
5115 | "when you created your account is included below.\n" | ||
5116 | "%s", | ||
3613 | Rd->Host, Rd->Host, Rd->Host, | 5117 | Rd->Host, Rd->Host, Rd->Host, |
3614 | getStrH(Rd->stuff, "email"), | 5118 | getStrH(Rd->stuff, "email"), |
3615 | Rd->Host, Rd->Host, | 5119 | Rd->Host, Rd->Host, |
3616 | first, last, | 5120 | first, last, |
3617 | first, last, Rd->Host, Rd->RUri, | 5121 | first, last, Rd->Host, Rd->RUri, |
3618 | Rd->Host, Rd->RUri, t1 | 5122 | Rd->Host, Rd->RUri, t1, ToS |
3619 | ); | 5123 | ); |
3620 | l = strlen(content); | 5124 | l = strlen(content); |
3621 | file = xmprintf("%s/sessions/%s.email", scCache, shs->leaf); | 5125 | file = xmprintf("%s/sessions/%s.email", scCache, shs->leaf); |
@@ -3628,11 +5132,8 @@ t("Write shs %s", tnm4); | |||
3628 | } | 5132 | } |
3629 | xclose(fd); | 5133 | xclose(fd); |
3630 | I("Sending linky email to %s %s", getStrH(Rd->stuff, "email"), t1); | 5134 | I("Sending linky email to %s %s", getStrH(Rd->stuff, "email"), t1); |
3631 | command = xmprintf("sendmail -oi -t <'%s'", file); | 5135 | if (shellMeFail("sendmail -oi -t <'%s'", file)) |
3632 | int i = system(command); | ||
3633 | if (!WIFEXITED(i)) | ||
3634 | E("sendmail command failed!"); | 5136 | E("sendmail command failed!"); |
3635 | free(command); | ||
3636 | free(file); | 5137 | free(file); |
3637 | free(content); | 5138 | free(content); |
3638 | free(t1); | 5139 | free(t1); |
@@ -3729,84 +5230,6 @@ systemFolders sysFolders[] = | |||
3729 | {"Trash", 14}, | 5230 | {"Trash", 14}, |
3730 | {NULL, -1} | 5231 | {NULL, -1} |
3731 | }; | 5232 | }; |
3732 | |||
3733 | boolean writeLuaDouble(reqData *Rd, int fd, char *file, char *name, double number) | ||
3734 | { | ||
3735 | boolean ret = TRUE; | ||
3736 | // TODO - putting these in Lua as numbers causes lua_tolstring to barf when we read them. Though Lua is supposed to convert between numbers and strings. | ||
3737 | char *t = xmprintf(" ['%s'] = '%f',\n", name, number); // NOTE - default precision is 6 decimal places. | ||
3738 | size_t l = strlen(t); | ||
3739 | |||
3740 | if (l != writeall(fd, t, l)) | ||
3741 | { | ||
3742 | perror_msg("Writing %s", file); | ||
3743 | ret = FALSE; | ||
3744 | } | ||
3745 | free(t); | ||
3746 | return ret; | ||
3747 | } | ||
3748 | |||
3749 | boolean writeLuaInteger(reqData *Rd, int fd, char *file, char *name, long number) | ||
3750 | { | ||
3751 | boolean ret = TRUE; | ||
3752 | // TODO - putting these in Lua as numbers causes lua_tolstring to barf when we read them. Though Lua is supposed to convert between numbers and strings. | ||
3753 | char *t = xmprintf(" ['%s'] = '%ld',\n", name, number); | ||
3754 | size_t l = strlen(t); | ||
3755 | |||
3756 | if (l != writeall(fd, t, l)) | ||
3757 | { | ||
3758 | perror_msg("Writing %s", file); | ||
3759 | ret = FALSE; | ||
3760 | } | ||
3761 | free(t); | ||
3762 | return ret; | ||
3763 | } | ||
3764 | |||
3765 | boolean writeLuaString(reqData *Rd, int fd, char *file, char *name, char *string) | ||
3766 | { | ||
3767 | boolean ret = TRUE; | ||
3768 | |||
3769 | if (NULL == string) | ||
3770 | string = getStrH(Rd->stuff, name); | ||
3771 | |||
3772 | size_t l = strlen(string); | ||
3773 | char *t0 = xmalloc(l * 2 + 1); | ||
3774 | int i, j = 0; | ||
3775 | |||
3776 | // TODO - maybe escape other non printables as well? | ||
3777 | for (i = 0; i < l; i++) | ||
3778 | { | ||
3779 | // We don't need to escape [] here, coz we are using '' below. Same applies to ", but do it anyway. | ||
3780 | switch(string[i]) | ||
3781 | { | ||
3782 | case '\n': | ||
3783 | case '\\': | ||
3784 | case '\'': | ||
3785 | case '"': | ||
3786 | t0[j++] = '\\'; break; | ||
3787 | } | ||
3788 | if ('\n' == string[i]) | ||
3789 | t0[j++] = 'n'; | ||
3790 | else if ('\r' == string[i]) | ||
3791 | ; | ||
3792 | else | ||
3793 | t0[j++] = string[i]; | ||
3794 | } | ||
3795 | t0[j] = '\0'; | ||
3796 | |||
3797 | char *t1 = xmprintf(" ['%s'] = '%s',\n", name, t0); | ||
3798 | |||
3799 | l = strlen(t1); | ||
3800 | if (l != writeall(fd, t1, l)) | ||
3801 | { | ||
3802 | perror_msg("Writing %s to %s", name, file); | ||
3803 | ret = FALSE; | ||
3804 | } | ||
3805 | free(t1); | ||
3806 | free(t0); | ||
3807 | return ret; | ||
3808 | } | ||
3809 | |||
3810 | static void accountWrite(reqData *Rd) | 5233 | static void accountWrite(reqData *Rd) |
3811 | { | 5234 | { |
3812 | char *uuid = getStrH(Rd->database, "UserAccounts.PrincipalID"); | 5235 | char *uuid = getStrH(Rd->database, "UserAccounts.PrincipalID"); |
@@ -3855,6 +5278,7 @@ static void accountWrite(reqData *Rd) | |||
3855 | if (!writeLuaString (Rd, fd, file, "aboutMe", NULL)) goto notWritten; | 5278 | if (!writeLuaString (Rd, fd, file, "aboutMe", NULL)) goto notWritten; |
3856 | if (!writeLuaString (Rd, fd, file, "vouched", "off")) goto notWritten; | 5279 | if (!writeLuaString (Rd, fd, file, "vouched", "off")) goto notWritten; |
3857 | if (!writeLuaString (Rd, fd, file, "voucher", NULL)) goto notWritten; | 5280 | if (!writeLuaString (Rd, fd, file, "voucher", NULL)) goto notWritten; |
5281 | if (!writeLuaString (Rd, fd, file, "approver", NULL)) goto notWritten; | ||
3858 | if (!writeLuaString (Rd, fd, file, "link", link)) goto notWritten; | 5282 | if (!writeLuaString (Rd, fd, file, "link", link)) goto notWritten; |
3859 | l = strlen(end); | 5283 | l = strlen(end); |
3860 | if (l != writeall(fd, end, l)) | 5284 | if (l != writeall(fd, end, l)) |
@@ -3900,7 +5324,7 @@ notWritten: | |||
3900 | "UserLevel", | 5324 | "UserLevel", |
3901 | "UserFlags", | 5325 | "UserFlags", |
3902 | "UserTitle", | 5326 | "UserTitle", |
3903 | // "ServiceURLs", // No worky "text", filled with crap. | 5327 | // "ServiceURLs", // No worky "text", filled with crap. Leaving it blank works fine. |
3904 | "active", | 5328 | "active", |
3905 | NULL | 5329 | NULL |
3906 | }; | 5330 | }; |
@@ -4051,41 +5475,31 @@ notWritten: | |||
4051 | } | 5475 | } |
4052 | 5476 | ||
4053 | // load iar -m first last / password /opt/opensim_SC/backups/DefaultMember.IAR | 5477 | // load iar -m first last / password /opt/opensim_SC/backups/DefaultMember.IAR |
4054 | simList *sims = getSims(); | ||
4055 | struct sysinfo info; | 5478 | struct sysinfo info; |
4056 | float la; | 5479 | float la; |
4057 | 5480 | ||
4058 | sysinfo(&info); | 5481 | sysinfo(&info); |
4059 | la = info.loads[0]/65536.0; | 5482 | la = info.loads[0]/65536.0; |
4060 | 5483 | ||
4061 | for (i = 0; i < sims->num; i++) | 5484 | char *name; |
4062 | { | 5485 | simData *simd = ourSims->byTab->get(ourSims->byTab, backupIARsim, NULL, false); |
4063 | char *sim = sims->sims[i], *name = getSimName(sims->sims[i]); | ||
4064 | 5486 | ||
4065 | if (checkSimIsRunning(sim)) | 5487 | if (NULL == simd) |
4066 | { | 5488 | E("Sim %s not found in ini list! Can't create inventory for %s %s", backupIARsim, first, last); |
4067 | I("Loading default member IAR for %s %s in sim %s, this might take a couple of minutes.", first, last, name); | 5489 | else |
4068 | char *c = xmprintf("%s %s/%s send-keys -t '%s:%d' 'load iar -m %s %s / password /opt/opensim_SC/backups/DefaultMember.IAR' Enter", | 5490 | name = simd->name; |
4069 | Tcmd, scRun, Tsocket, Tconsole, i + 1, first, last); | 5491 | |
4070 | T(c); | 5492 | if (checkSimIsRunning(backupIARsim)) |
4071 | int r = system(c); | 5493 | { |
4072 | if (!WIFEXITED(r)) | 5494 | char *cmd = xmprintf("%d.%d", simd->window, simd->pane); |
4073 | E("tmux load iar command failed!"); | 5495 | char *c = xmprintf("load iar -m %s %s / password /opt/opensim_SC/backups/DefaultMember.IAR", first, last); |
4074 | else | 5496 | |
4075 | { | 5497 | I("Loading default member IAR for %s %s in sim %s, this might take a couple of minutes.", first, last, name); |
4076 | // memset(toybuf, 0, sizeof(toybuf)); | 5498 | V(c); |
4077 | // snprintf(toybuf, sizeof(toybuf), "INITIALIZATION COMPLETE FOR %s", name); | 5499 | sendTmuxCmd(cmd, c); |
4078 | // waitTmuxText(name, toybuf); | 5500 | free(cmd); |
4079 | // I("%s is done starting up.", name); | 5501 | free(c); |
4080 | // la = waitLoadAverage(la, loadAverageInc, simTimeOut); | ||
4081 | } | ||
4082 | free(c); | ||
4083 | free(name); | ||
4084 | break; | ||
4085 | } | ||
4086 | free(name); | ||
4087 | } | 5502 | } |
4088 | freeSimList(sims); | ||
4089 | } | 5503 | } |
4090 | free(first); | 5504 | free(first); |
4091 | } | 5505 | } |
@@ -4202,15 +5616,6 @@ Maybe - | |||
4202 | DAVEE (Delete, Add, View, Edit, Explore) | 5616 | DAVEE (Delete, Add, View, Edit, Explore) |
4203 | */ | 5617 | */ |
4204 | 5618 | ||
4205 | // lua.h has LUA_T* NONE, NIL, BOOLEAN, LIGHTUSERDATA, NUMBER, STRING, TABLE, FUNCTION, USERDATA, THREAD as defines, -1 - 8. | ||
4206 | // These are the missing ones. Then later we will have prim, mesh, script, sound, terrain, ... | ||
4207 | #define LUA_TGROUP 42 | ||
4208 | #define LUA_TINTEGER 43 | ||
4209 | #define LUA_TEMAIL 44 | ||
4210 | #define LUA_TPASSWORD 45 | ||
4211 | #define LUA_TFILE 46 | ||
4212 | #define LUA_TIMAGE 47 | ||
4213 | |||
4214 | #define FLD_NONE 0 | 5619 | #define FLD_NONE 0 |
4215 | #define FLD_EDITABLE 1 | 5620 | #define FLD_EDITABLE 1 |
4216 | #define FLD_HIDDEN 2 | 5621 | #define FLD_HIDDEN 2 |
@@ -4757,8 +6162,8 @@ static int DoBValidate(reqData *Rd, inputForm *iF, inputValue *iV) | |||
4757 | else | 6162 | else |
4758 | { | 6163 | { |
4759 | i = atoi(t0); | 6164 | i = atoi(t0); |
4760 | // TODO - get this to use current year instead of 2020. | 6165 | // TODO - get this to use current year instead of 2021. |
4761 | if ((1900 > i) || (i > 2020)) | 6166 | if ((1900 > i) || (i > 2021)) |
4762 | { | 6167 | { |
4763 | bitch(Rd, "Please supply a year of birth.", "Out of range."); | 6168 | bitch(Rd, "Please supply a year of birth.", "Out of range."); |
4764 | ret++; | 6169 | ret++; |
@@ -4768,7 +6173,7 @@ static int DoBValidate(reqData *Rd, inputForm *iF, inputValue *iV) | |||
4768 | bitch(Rd, "Please supply a proper year of birth.", "Out of range, too old."); | 6173 | bitch(Rd, "Please supply a proper year of birth.", "Out of range, too old."); |
4769 | ret++; | 6174 | ret++; |
4770 | } | 6175 | } |
4771 | else if (i >2004) | 6176 | else if (i >2005) |
4772 | { | 6177 | { |
4773 | bitch(Rd, "This grid is Adult rated, you are too young.", "Out of range, too young."); | 6178 | bitch(Rd, "This grid is Adult rated, you are too young.", "Out of range, too young."); |
4774 | ret++; | 6179 | ret++; |
@@ -5035,6 +6440,7 @@ static void accountViewWeb(reqData *Rd, inputForm *oF, inputValue *oV) | |||
5035 | *level = getStrH(Rd->database, "UserAccounts.UserLevel"), | 6440 | *level = getStrH(Rd->database, "UserAccounts.UserLevel"), |
5036 | *email = getStrH(Rd->database, "UserAccounts.Email"), | 6441 | *email = getStrH(Rd->database, "UserAccounts.Email"), |
5037 | *voucher = getStrH(Rd->database, "Lua.voucher"), | 6442 | *voucher = getStrH(Rd->database, "Lua.voucher"), |
6443 | *approver = getStrH(Rd->database, "Lua.approver"), | ||
5038 | *about = getStrH(Rd->database, "Lua.aboutMe"); | 6444 | *about = getStrH(Rd->database, "Lua.aboutMe"); |
5039 | time_t crtd = atol(getStrH(Rd->database, "UserAccounts.Created")); | 6445 | time_t crtd = atol(getStrH(Rd->database, "UserAccounts.Created")); |
5040 | 6446 | ||
@@ -5047,6 +6453,7 @@ static void accountViewWeb(reqData *Rd, inputForm *oF, inputValue *oV) | |||
5047 | Rd->reply->addstrf(Rd->reply, "<p><font size='5'><span style='font-size: x-large'><b>Email :</b></span></font> %s</p>", email); | 6453 | Rd->reply->addstrf(Rd->reply, "<p><font size='5'><span style='font-size: x-large'><b>Email :</b></span></font> %s</p>", email); |
5048 | Rd->reply->addstrf(Rd->reply, "<p><font size='5'><span style='font-size: x-large'><b>UUID :</b></span></font> %s</p>", getStrH(Rd->database, "UserAccounts.PrincipalID")); | 6454 | Rd->reply->addstrf(Rd->reply, "<p><font size='5'><span style='font-size: x-large'><b>UUID :</b></span></font> %s</p>", getStrH(Rd->database, "UserAccounts.PrincipalID")); |
5049 | Rd->reply->addstrf(Rd->reply, "<p><font size='5'><span style='font-size: x-large'><b>Voucher :</b></span></font> %s</p>", voucher); | 6455 | Rd->reply->addstrf(Rd->reply, "<p><font size='5'><span style='font-size: x-large'><b>Voucher :</b></span></font> %s</p>", voucher); |
6456 | Rd->reply->addstrf(Rd->reply, "<p><font size='5'><span style='font-size: x-large'><b>Approver :</b></span></font> %s</p>", approver); | ||
5050 | HTMLtextArea(Rd->reply, "aboutMe", "About", 7, 50, 4, 16384, "", "off", "true", "soft", about, FALSE, TRUE); | 6457 | HTMLtextArea(Rd->reply, "aboutMe", "About", 7, 50, 4, 16384, "", "off", "true", "soft", about, FALSE, TRUE); |
5051 | accountWebSubs(Rd, oF); | 6458 | accountWebSubs(Rd, oF); |
5052 | accountWebFooter(Rd, oF); | 6459 | accountWebFooter(Rd, oF); |
@@ -5058,6 +6465,7 @@ static void accountEditWeb(reqData *Rd, inputForm *oF, inputValue *oV) | |||
5058 | *level = getStrH(Rd->database, "UserAccounts.UserLevel"), | 6465 | *level = getStrH(Rd->database, "UserAccounts.UserLevel"), |
5059 | *email = getStrH(Rd->database, "UserAccounts.Email"), | 6466 | *email = getStrH(Rd->database, "UserAccounts.Email"), |
5060 | *voucher = getStrH(Rd->database, "Lua.voucher"), | 6467 | *voucher = getStrH(Rd->database, "Lua.voucher"), |
6468 | *approver = getStrH(Rd->database, "Lua.approver"), | ||
5061 | *about = getStrH(Rd->database, "Lua.aboutMe"), | 6469 | *about = getStrH(Rd->database, "Lua.aboutMe"), |
5062 | *lvl = getLevel(atoi(level)); | 6470 | *lvl = getLevel(atoi(level)); |
5063 | short lv = atoi(level); | 6471 | short lv = atoi(level); |
@@ -5078,6 +6486,7 @@ static void accountEditWeb(reqData *Rd, inputForm *oF, inputValue *oV) | |||
5078 | { | 6486 | { |
5079 | qlisttbl_obj_t obj; | 6487 | qlisttbl_obj_t obj; |
5080 | 6488 | ||
6489 | HTMLhidden(Rd->reply, "approver", approver); | ||
5081 | HTMLselect(Rd->reply, "level", "level"); | 6490 | HTMLselect(Rd->reply, "level", "level"); |
5082 | memset((void*)&obj, 0, sizeof(obj)); // must be cleared before call | 6491 | memset((void*)&obj, 0, sizeof(obj)); // must be cleared before call |
5083 | accountLevels->lock(accountLevels); | 6492 | accountLevels->lock(accountLevels); |
@@ -5228,7 +6637,7 @@ d("accountRead() UUID %s, name %s %s", uuid, first, last); | |||
5228 | 6637 | ||
5229 | if (0 == rt) | 6638 | if (0 == rt) |
5230 | { | 6639 | { |
5231 | T("Found Lua record."); | 6640 | V("Found Lua record."); |
5232 | ret += 1; | 6641 | ret += 1; |
5233 | Rd->database->putstr(Rd->database, "UserAccounts.FirstName", first); | 6642 | Rd->database->putstr(Rd->database, "UserAccounts.FirstName", first); |
5234 | Rd->database->putstr(Rd->database, "UserAccounts.LastName", last); | 6643 | Rd->database->putstr(Rd->database, "UserAccounts.LastName", last); |
@@ -5249,6 +6658,7 @@ T("Found Lua record."); | |||
5249 | Rd->database->putstr(Rd->database, "Lua.aboutMe", getStrH(tnm, "aboutMe")); | 6658 | Rd->database->putstr(Rd->database, "Lua.aboutMe", getStrH(tnm, "aboutMe")); |
5250 | Rd->database->putstr(Rd->database, "Lua.vouched", getStrH(tnm, "vouched")); | 6659 | Rd->database->putstr(Rd->database, "Lua.vouched", getStrH(tnm, "vouched")); |
5251 | Rd->database->putstr(Rd->database, "Lua.voucher", getStrH(tnm, "voucher")); | 6660 | Rd->database->putstr(Rd->database, "Lua.voucher", getStrH(tnm, "voucher")); |
6661 | Rd->database->putstr(Rd->database, "Lua.approver", getStrH(tnm, "approver")); | ||
5252 | } | 6662 | } |
5253 | // else if (rows) | 6663 | // else if (rows) |
5254 | if (rows) | 6664 | if (rows) |
@@ -5257,7 +6667,7 @@ T("Found Lua record."); | |||
5257 | if (1 == rt) | 6667 | if (1 == rt) |
5258 | { | 6668 | { |
5259 | ret = rt; | 6669 | ret = rt; |
5260 | T("Found database record."); | 6670 | V("Found database record."); |
5261 | dbPull(Rd, "UserAccounts", rows); | 6671 | dbPull(Rd, "UserAccounts", rows); |
5262 | 6672 | ||
5263 | char *name = xmprintf("%s %s", getStrH(Rd->database, "UserAccounts.FirstName"), getStrH(Rd->database, "UserAccounts.LastName")); | 6673 | char *name = xmprintf("%s %s", getStrH(Rd->database, "UserAccounts.FirstName"), getStrH(Rd->database, "UserAccounts.LastName")); |
@@ -5330,9 +6740,9 @@ static int accountDelSub(reqData *Rd, inputForm *iF, inputValue *iV) | |||
5330 | } | 6740 | } |
5331 | else | 6741 | else |
5332 | { | 6742 | { |
5333 | // check if logged in user is allowed to delete this account | 6743 | // TODO - check if logged in user is allowed to delete this account |
5334 | // delete user record | 6744 | // TODO - delete user record |
5335 | // log the user out if they are logged in | 6745 | // TODO - log the user out if they are logged in |
5336 | } | 6746 | } |
5337 | return ret; | 6747 | return ret; |
5338 | } | 6748 | } |
@@ -5425,7 +6835,8 @@ static int accountSaveSub(reqData *Rd, inputForm *iF, inputValue *iV) | |||
5425 | { | 6835 | { |
5426 | int ret = 0; | 6836 | int ret = 0; |
5427 | // Using body[user] here, coz we got to this page via a URL query. | 6837 | // Using body[user] here, coz we got to this page via a URL query. |
5428 | char *uuid = Rd->shs.UUID, *first = getStrH(Rd->body, "user"), *last = NULL; | 6838 | char *uuid = Rd->shs.UUID, *first = getStrH(Rd->body, "user"), *last = NULL, |
6839 | *level = getStrH(Rd->database, "UserAccounts.UserLevel"); | ||
5429 | int c = accountRead(Rd, NULL, first, last); | 6840 | int c = accountRead(Rd, NULL, first, last); |
5430 | 6841 | ||
5431 | if (1 != c) | 6842 | if (1 != c) |
@@ -5435,6 +6846,24 @@ static int accountSaveSub(reqData *Rd, inputForm *iF, inputValue *iV) | |||
5435 | } | 6846 | } |
5436 | else if ((0 == ret) && (strcmp("POST", Rd->Method) == 0)) | 6847 | else if ((0 == ret) && (strcmp("POST", Rd->Method) == 0)) |
5437 | { | 6848 | { |
6849 | char *lvl = getStrH(Rd->body, "level"); | ||
6850 | qlisttbl_obj_t obj; | ||
6851 | |||
6852 | if (strcmp(level, lvl) != 0) | ||
6853 | { | ||
6854 | if (200 <= Rd->shs.level) | ||
6855 | { | ||
6856 | I("%s %s approved by %s.", getStrH(Rd->database, "UserAccounts.FirstName"), getStrH(Rd->database, "UserAccounts.LastName"), Rd->shs.name); | ||
6857 | Rd->stuff->putstr(Rd->stuff, "approver", Rd->shs.name); | ||
6858 | } | ||
6859 | else | ||
6860 | { | ||
6861 | bitch(Rd, "Cannot change level.", "User level not high enough."); | ||
6862 | lvl = level; | ||
6863 | Rd->stuff->putstr(Rd->stuff, "approver", getStrH(Rd->database, "Lua.approver")); | ||
6864 | } | ||
6865 | } | ||
6866 | |||
5438 | Rd->stuff->putstr(Rd->stuff, "email", getStrH(Rd->database, "UserAccounts.Email")); | 6867 | Rd->stuff->putstr(Rd->stuff, "email", getStrH(Rd->database, "UserAccounts.Email")); |
5439 | Rd->stuff->putstr(Rd->stuff, "created", getStrH(Rd->database, "UserAccounts.Created")); | 6868 | Rd->stuff->putstr(Rd->stuff, "created", getStrH(Rd->database, "UserAccounts.Created")); |
5440 | Rd->stuff->putstr(Rd->stuff, "flags", getStrH(Rd->database, "UserAccounts.UserFlags")); | 6869 | Rd->stuff->putstr(Rd->stuff, "flags", getStrH(Rd->database, "UserAccounts.UserFlags")); |
@@ -5449,15 +6878,14 @@ static int accountSaveSub(reqData *Rd, inputForm *iF, inputValue *iV) | |||
5449 | Rd->stuff->putstr(Rd->stuff, "vouched", getStrH(Rd->database, "Lua.vouched")); | 6878 | Rd->stuff->putstr(Rd->stuff, "vouched", getStrH(Rd->database, "Lua.vouched")); |
5450 | Rd->stuff->putstr(Rd->stuff, "voucher", getStrH(Rd->database, "Lua.voucher")); | 6879 | Rd->stuff->putstr(Rd->stuff, "voucher", getStrH(Rd->database, "Lua.voucher")); |
5451 | 6880 | ||
5452 | char *lvl = getStrH(Rd->body, "level"); | ||
5453 | qlisttbl_obj_t obj; | ||
5454 | |||
5455 | memset((void*)&obj, 0, sizeof(obj)); // must be cleared before call | 6881 | memset((void*)&obj, 0, sizeof(obj)); // must be cleared before call |
5456 | accountLevels->lock(accountLevels); | 6882 | accountLevels->lock(accountLevels); |
5457 | while(accountLevels->getnext(accountLevels, &obj, NULL, false) == true) | 6883 | while(accountLevels->getnext(accountLevels, &obj, NULL, false) == true) |
5458 | { | 6884 | { |
5459 | if (strcmp(lvl, (char *) obj.data) == 0) | 6885 | if (strcmp(lvl, (char *) obj.data) == 0) |
6886 | { | ||
5460 | Rd->database->putstr(Rd->database, "UserAccounts.UserLevel", obj.name); | 6887 | Rd->database->putstr(Rd->database, "UserAccounts.UserLevel", obj.name); |
6888 | } | ||
5461 | } | 6889 | } |
5462 | accountLevels->unlock(accountLevels); | 6890 | accountLevels->unlock(accountLevels); |
5463 | accountWrite(Rd); | 6891 | accountWrite(Rd); |
@@ -5496,6 +6924,7 @@ static int accountValidateSub(reqData *Rd, inputForm *iF, inputValue *iV) | |||
5496 | Rd->stuff->putstr(Rd->stuff, "aboutMe", getStrH(Rd->database, "Lua.aboutMe")); | 6924 | Rd->stuff->putstr(Rd->stuff, "aboutMe", getStrH(Rd->database, "Lua.aboutMe")); |
5497 | Rd->stuff->putstr(Rd->stuff, "vouched", getStrH(Rd->database, "Lua.vouched")); | 6925 | Rd->stuff->putstr(Rd->stuff, "vouched", getStrH(Rd->database, "Lua.vouched")); |
5498 | Rd->stuff->putstr(Rd->stuff, "voucher", getStrH(Rd->database, "Lua.voucher")); | 6926 | Rd->stuff->putstr(Rd->stuff, "voucher", getStrH(Rd->database, "Lua.voucher")); |
6927 | Rd->stuff->putstr(Rd->stuff, "approver", getStrH(Rd->database, "Lua.approver")); | ||
5499 | Rd->shs.level = -100; | 6928 | Rd->shs.level = -100; |
5500 | Rd->database->putstr(Rd->database, "UserAccounts.UserLevel", "-100"); | 6929 | Rd->database->putstr(Rd->database, "UserAccounts.UserLevel", "-100"); |
5501 | accountWrite(Rd); | 6930 | accountWrite(Rd); |
@@ -5564,8 +6993,8 @@ d("Sub accountEditSub %s %s %s", uuid, first, last); | |||
5564 | } | 6993 | } |
5565 | else | 6994 | else |
5566 | { | 6995 | { |
5567 | // check if logged in user is allowed to make these changes | 6996 | // TODO - check if logged in user is allowed to make these changes |
5568 | // update user record | 6997 | // TODO - update user record |
5569 | } | 6998 | } |
5570 | return ret; | 6999 | return ret; |
5571 | } | 7000 | } |
@@ -6330,11 +7759,96 @@ void account_html(char *file, reqData *Rd, HTMLfile *thisFile) | |||
6330 | } | 7759 | } |
6331 | 7760 | ||
6332 | 7761 | ||
7762 | void forEachMember(char *verb, simFunction func, simFunction not) | ||
7763 | { | ||
7764 | char *last = xstrdup("0"); | ||
7765 | |||
7766 | if (BACKUP == currentMode) | ||
7767 | { | ||
7768 | char *file = xmprintf("%s/.lastTime", scBackup); | ||
7769 | |||
7770 | free(last); | ||
7771 | last = (char *) qfile_load(file, NULL); | ||
7772 | if (shellMeFail("date +%%s > %s", file)) | ||
7773 | E("date command failed!"); | ||
7774 | free(file); | ||
7775 | if ((NULL == last) || (NULL != ourSims->target)) | ||
7776 | { | ||
7777 | free(last); | ||
7778 | last = xstrdup("0"); | ||
7779 | } | ||
7780 | } | ||
7781 | |||
7782 | static dbRequest *users = NULL; | ||
7783 | if (NULL == users) | ||
7784 | { | ||
7785 | static char *szi[] = {NULL}; | ||
7786 | static char *szo[] = {"FirstName", "LastName", "UserLevel", "Online", "LastRegionID", NULL}; | ||
7787 | users = xzalloc(sizeof(dbRequest)); | ||
7788 | users->table = "UserAccounts,GridUser"; | ||
7789 | users->inParams = szi; | ||
7790 | users->outParams = szo; | ||
7791 | users->where = xmprintf("UserAccounts.PrincipalID=LEFT(GridUser.UserID,36) and GridUser.Login>%s order by LastName,FirstName", last); | ||
7792 | dbRequests->addfirst(dbRequests, &users, sizeof(dbRequest *)); | ||
7793 | } | ||
7794 | |||
7795 | dbDoSomething(users, FALSE); | ||
7796 | rowData *rows = users->rows; | ||
7797 | if (rows) | ||
7798 | { | ||
7799 | qhashtbl_t *row; | ||
7800 | |||
7801 | while (NULL != (row = rows->rows->getat(rows->rows, 0, NULL, true))) | ||
7802 | { | ||
7803 | char *firstName = row->getstr(row, "FirstName", false); | ||
7804 | char *lastName = row->getstr(row, "LastName", false); | ||
7805 | char *name = xmprintf("%s %s", firstName, lastName); | ||
7806 | |||
7807 | if ((NULL == ourSims->target) || (strcmp(ourSims->target, name) == 0)) | ||
7808 | { | ||
7809 | if (STATUS == currentMode) | ||
7810 | { | ||
7811 | char *level = row->getstr(row, "UserLevel", false); | ||
7812 | char *online = row->getstr(row, "Online", false); | ||
7813 | char *region = row->getstr(row, "LastRegionID", false); | ||
7814 | simData *simd = ourSims->byUUID->get(ourSims->byUUID, region, NULL, false); | ||
7815 | |||
7816 | if (NULL != simd) | ||
7817 | region = simd->name; | ||
7818 | if (strcmp("False", online) == 0) | ||
7819 | region = ""; | ||
7820 | |||
7821 | I("Member level %s, online %s, name %s %s @ %s", level, online, firstName, lastName, region); | ||
7822 | } | ||
7823 | func(NULL, name, "Member", 0, 0, 0, 0); | ||
7824 | } | ||
7825 | |||
7826 | free(name); | ||
7827 | row->free(row); | ||
7828 | rows->rows->removefirst(rows->rows); | ||
7829 | } | ||
7830 | free(rows->fieldNames); | ||
7831 | rows->rows->free(rows->rows); | ||
7832 | free(rows); | ||
7833 | } | ||
7834 | free(last); | ||
7835 | } | ||
7836 | |||
7837 | |||
6333 | static void cleanup(void) | 7838 | static void cleanup(void) |
6334 | { | 7839 | { |
6335 | // TODO - not sure why, but this gets called twice on quitting sometimes. | ||
6336 | C("Caught signal, or quitting, cleaning up."); | 7840 | C("Caught signal, or quitting, cleaning up."); |
6337 | 7841 | ||
7842 | char *cmd = xmprintf("%s/sledjchisl.socket", scRun); | ||
7843 | |||
7844 | if (isWeb && qfile_exist(cmd)) | ||
7845 | { | ||
7846 | V("Deleting web socket."); | ||
7847 | if (shellMeFail("rm %s/sledjchisl.socket", scRun)) | ||
7848 | E("rm command failed!"); | ||
7849 | } | ||
7850 | free(cmd); | ||
7851 | |||
6338 | if (accountPages) | 7852 | if (accountPages) |
6339 | { | 7853 | { |
6340 | qhashtbl_obj_t obj; | 7854 | qhashtbl_obj_t obj; |
@@ -6396,32 +7910,75 @@ static void cleanup(void) | |||
6396 | } | 7910 | } |
6397 | if (configs) configs->free(configs); | 7911 | if (configs) configs->free(configs); |
6398 | configs = NULL; | 7912 | configs = NULL; |
7913 | |||
7914 | /* tmux should clean up it's own socket. Doesn't seem to care anyway. | ||
7915 | cmd = xmprintf("%s/%s", scRun, Tsocket); | ||
7916 | if (qfile_exist(cmd)) | ||
7917 | { | ||
7918 | if (shellMeFail("rm %s/%s", scRun, Tsocket)) | ||
7919 | E("rm command failed!"); | ||
7920 | } | ||
7921 | free(cmd); | ||
7922 | */ | ||
6399 | } | 7923 | } |
6400 | 7924 | ||
6401 | 7925 | ||
7926 | int scanForConfigs(char **cPaths) | ||
7927 | { | ||
7928 | int result = FALSE, i; | ||
7929 | struct stat st; | ||
7930 | |||
7931 | for (i = 0; cPaths[i]; i++) | ||
7932 | { | ||
7933 | if (('/' == cPaths[i][0]) || ('~' == cPaths[i][0])) | ||
7934 | snprintf(toybuf, sizeof(toybuf), "%s", cPaths[i]); | ||
7935 | else | ||
7936 | snprintf(toybuf, sizeof(toybuf), "%s/%s", pwd, cPaths[i]); | ||
7937 | |||
7938 | if (qfile_exist(toybuf)) | ||
7939 | { | ||
7940 | I("Loading configuration file (maybe) - %s", toybuf); | ||
7941 | Lua2hashtbl(toybuf, configs, "config"); | ||
7942 | result = TRUE; | ||
7943 | } | ||
7944 | } | ||
7945 | return result; | ||
7946 | } | ||
7947 | |||
7948 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
7949 | // Main function. | ||
7950 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
6402 | void sledjchisl_main(void) | 7951 | void sledjchisl_main(void) |
6403 | { | 7952 | { |
6404 | char *cmd = *toys.optargs; | 7953 | char *cmd = *toys.optargs; |
6405 | char *tmp; | 7954 | char *tmp, *t0 = NULL, *t1 = NULL; |
6406 | struct stat statbuf; | 7955 | struct stat statbuf; |
6407 | int status, result, i; | 7956 | int status, result, i; |
6408 | void *vd; | 7957 | void *vd; |
6409 | 7958 | ||
7959 | if(FLAG(v)) | ||
7960 | VERBOSE = TRUE; | ||
7961 | |||
7962 | V("SledjChisl command - %s", toys.argv[0]); | ||
7963 | V("SledjChisl arguments - %d %d", toys.optc, toys.optflags); | ||
7964 | for (i = 0; i < toys.optc; i++) | ||
7965 | V(" argument %d %s", i, toys.optargs[i] ); | ||
7966 | |||
6410 | configs = qhashtbl(0, 0); | 7967 | configs = qhashtbl(0, 0); |
6411 | L = luaL_newstate(); | 7968 | L = luaL_newstate(); |
6412 | 7969 | ||
6413 | I("libfcgi version: %s", FCGI_VERSION); | 7970 | V("libfcgi version: %s", FCGI_VERSION); |
6414 | I("Lua version: %s", LUA_RELEASE); | 7971 | V("Lua version: %s", LUA_RELEASE); |
6415 | I("LuaJIT version: %s", LUAJIT_VERSION); | 7972 | V("LuaJIT version: %s", LUAJIT_VERSION); |
6416 | I("MariaDB / MySQL client version: %s", mysql_get_client_info()); | 7973 | V("MariaDB / MySQL client version: %s", mysql_get_client_info()); |
6417 | I("OpenSSL version: %s", OPENSSL_VERSION_TEXT); | 7974 | V("OpenSSL version: %s", OPENSSL_VERSION_TEXT); |
6418 | I("qLibc version: qLibc only git tags for version numbers. Sooo, 2.4.4, unless I forgot to update this."); | 7975 | V("qLibc version: qLibc only git tags for version numbers. Sooo, 2.4.4, unless I forgot to update this."); |
6419 | I("toybox version: %s", TOYBOX_VERSION); | 7976 | V("toybox version: %s", TOYBOX_VERSION); |
6420 | |||
6421 | dbRequests = qlist(0); | 7977 | dbRequests = qlist(0); |
6422 | sigatexit(cleanup); | 7978 | sigatexit(cleanup); |
6423 | 7979 | ||
6424 | pwd = getcwd(0, 0); | 7980 | pwd = xgetcwd(); |
7981 | V("Running in directory %s", pwd); | ||
6425 | 7982 | ||
6426 | if (-1 == fstat(STDIN_FILENO, &statbuf)) | 7983 | if (-1 == fstat(STDIN_FILENO, &statbuf)) |
6427 | { | 7984 | { |
@@ -6453,23 +8010,26 @@ void sledjchisl_main(void) | |||
6453 | int ngroups; | 8010 | int ngroups; |
6454 | 8011 | ||
6455 | pw = xgetpwuid(euid); | 8012 | pw = xgetpwuid(euid); |
6456 | I("Running as user %s", pw->pw_name); | 8013 | V("Running as user %s", pw->pw_name); |
6457 | 8014 | ||
6458 | grp = xgetgrgid(egid); | 8015 | grp = xgetgrgid(egid); |
6459 | ngroups = getgroups(i, groups); | 8016 | ngroups = getgroups(i, groups); |
6460 | if (ngroups < 0) perror_exit("getgroups"); | 8017 | if (ngroups < 0) perror_exit("getgroups"); |
6461 | D("User is in group %s", grp->gr_name); | 8018 | V("User is in group %s", grp->gr_name); |
6462 | for (i = 0; i < ngroups; i++) { | 8019 | for (i = 0; i < ngroups; i++) { |
6463 | if (groups[i] != egid) | 8020 | if (groups[i] != egid) |
6464 | { | 8021 | { |
6465 | if ((grp = getgrgid(groups[i]))) | 8022 | if ((grp = getgrgid(groups[i]))) |
6466 | D("User is in group %s", grp->gr_name); | 8023 | V("User is in group %s", grp->gr_name); |
6467 | else | 8024 | else |
6468 | D("User is in group %u", groups[i]); | 8025 | V("User is in group %u", groups[i]); |
6469 | } | 8026 | } |
6470 | } | 8027 | } |
6471 | 8028 | ||
6472 | 8029 | ||
8030 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
8031 | // Read configuration. | ||
8032 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
6473 | /* From http://luajit.org/install.html - | 8033 | /* From http://luajit.org/install.html - |
6474 | To change or extend the list of standard libraries to load, copy | 8034 | To change or extend the list of standard libraries to load, copy |
6475 | src/lib_init.c to your project and modify it accordingly. Make sure the | 8035 | src/lib_init.c to your project and modify it accordingly. Make sure the |
@@ -6477,88 +8037,137 @@ jit library is loaded or the JIT compiler will not be activated. | |||
6477 | */ | 8037 | */ |
6478 | luaL_openlibs(L); // Load Lua libraries. | 8038 | luaL_openlibs(L); // Load Lua libraries. |
6479 | 8039 | ||
6480 | // Load the config scripts. | 8040 | /* Load the config scripts. |
8041 | Normally we start in /opt/opensim_SC/current/bin | ||
8042 | sledjchisl here is a symlink to ../src/build/toybox/toybox or ../src/build/toybox/generated/unstripped/toybox | ||
8043 | So that .sledjChisl.conf.lua is found, which is normally a symlink to ../src/.sledjChisl.conf.lua | ||
8044 | |||
8045 | Other possibilities - | ||
8046 | Normally sledjchisl isn't in the path, though it is once our tmux is running. | ||
8047 | No doubt someone will put it in the path, so then we can start anywhere. | ||
8048 | Someone could run it from a downloaded opensim-SC and run it from that bin, then it's not installed. | ||
8049 | */ | ||
6481 | char *cPaths[] = | 8050 | char *cPaths[] = |
6482 | { | 8051 | { |
6483 | ".sledjChisl.conf.lua", | ||
6484 | "/etc/sledjChisl.conf.lua", | 8052 | "/etc/sledjChisl.conf.lua", |
6485 | // "/etc/sledjChisl.d/*.lua", | 8053 | // "/etc/sledjChisl.d/*.lua", |
6486 | "~/.sledjChisl.conf.lua", | 8054 | "~/.sledjChisl.conf.lua", |
6487 | // "~/.config/sledjChisl/*.lua", | 8055 | // "~/.config/sledjChisl/*.lua", |
6488 | NULL | 8056 | NULL |
6489 | }; | 8057 | }; |
6490 | struct stat st; | ||
6491 | 8058 | ||
8059 | char *oPaths[] = | ||
8060 | { | ||
8061 | ".sledjChisl.conf.lua", | ||
8062 | "./etc/sledjChisl.conf.lua", | ||
8063 | NULL | ||
8064 | }; | ||
6492 | 8065 | ||
6493 | for (i = 0; cPaths[i]; i++) | 8066 | result = scanForConfigs(cPaths); |
8067 | result = result | scanForConfigs(oPaths); | ||
8068 | if (result) | ||
6494 | { | 8069 | { |
6495 | memset(toybuf, 0, sizeof(toybuf)); | 8070 | if ((tmp = configs->getstr(configs, "scRoot", false)) != NULL) {scRoot = tmp; V("Setting scRoot = %s", scRoot);} |
6496 | if (('/' == cPaths[i][0]) || ('~' == cPaths[i][0])) | 8071 | snprintf(toybuf, sizeof(toybuf), "%s/current/bin", scRoot); |
6497 | snprintf(toybuf, sizeof(toybuf), "%s", cPaths[i]); | 8072 | t0 = realpath(pwd, NULL); |
6498 | else | 8073 | t1 = realpath(toybuf, NULL); |
6499 | snprintf(toybuf, sizeof(toybuf), "%s/%s", pwd, cPaths[i]); | 8074 | if (strcmp(t0, t1) != 0) |
6500 | if (0 != lstat(toybuf, &st)) | ||
6501 | continue; | ||
6502 | I("Loading configuration file - %s", toybuf); | ||
6503 | status = luaL_loadfile(L, toybuf); | ||
6504 | if (status) // If something went wrong, error message is at the top of the stack. | ||
6505 | E("Couldn't load file: %s", lua_tostring(L, -1)); | ||
6506 | else | ||
6507 | { | 8075 | { |
6508 | result = lua_pcall(L, 0, LUA_MULTRET, 0); | 8076 | I("Not in the configured scRoot. %s != %s", t0, t1); |
6509 | if (result) | 8077 | if (qfile_exist(toybuf)) |
6510 | E("Failed to run script: %s", lua_tostring(L, -1)); | 8078 | { |
8079 | chdir(toybuf); | ||
8080 | free(pwd); | ||
8081 | pwd = xgetcwd(); | ||
8082 | I("Now running in directory %s", pwd); | ||
8083 | free(t0); | ||
8084 | t0 = realpath(pwd, NULL); | ||
8085 | result = scanForConfigs(cPaths); | ||
8086 | result = result | scanForConfigs(oPaths); | ||
8087 | } | ||
6511 | else | 8088 | else |
6512 | { | 8089 | { |
6513 | lua_getglobal(L, "config"); | 8090 | C("The configured scRoot doesn't exist! Might need to actually install this. %s", scRoot); |
6514 | lua_pushnil(L); | 8091 | goto finished; |
6515 | while(lua_next(L, -2) != 0) | ||
6516 | { | ||
6517 | char *n = (char *) lua_tostring(L, -2); | ||
6518 | |||
6519 | // Numbers can convert to strings, so check for numbers before checking for strings. | ||
6520 | // On the other hand, strings that can be converted to numbers also pass lua_isnumber(). sigh | ||
6521 | if (lua_isnumber(L, -1)) | ||
6522 | { | ||
6523 | float v = lua_tonumber(L, -1); | ||
6524 | configs->put(configs, n, &v, sizeof(float)); | ||
6525 | } | ||
6526 | else if (lua_isstring(L, -1)) | ||
6527 | configs->putstr(configs, n, (char *) lua_tostring(L, -1)); | ||
6528 | else if (lua_isboolean(L, -1)) | ||
6529 | { | ||
6530 | int v = lua_toboolean(L, -1); | ||
6531 | configs->putint(configs, n, v); | ||
6532 | } | ||
6533 | else | ||
6534 | { | ||
6535 | char *v = (char *) lua_tostring(L, -1); | ||
6536 | E("Unknown config variable type for %s = %s", n, v); | ||
6537 | } | ||
6538 | lua_pop(L, 1); | ||
6539 | } | ||
6540 | } | 8092 | } |
6541 | } | 8093 | } |
8094 | free(t1); | ||
6542 | } | 8095 | } |
6543 | DEBUG = configs->getint(configs, "debug"); | ||
6544 | D("Setting DEBUG = %d", DEBUG); | ||
6545 | if ((vd = configs->get (configs, "loadAverageInc", NULL, false)) != NULL) {loadAverageInc = *((float *) vd); D("Setting loadAverageInc = %f", loadAverageInc);} | ||
6546 | if ((vd = configs->get (configs, "simTimeOut", NULL, false)) != NULL) {simTimeOut = (int) *((float *) vd); D("Setting simTimeOut = %d", simTimeOut);} | ||
6547 | if ((tmp = configs->getstr(configs, "scRoot", false)) != NULL) {scRoot = tmp; D("Setting scRoot = %s", scRoot);} | ||
6548 | if ((tmp = configs->getstr(configs, "scUser", false)) != NULL) {scUser = tmp; D("Setting scUser = %s", scUser);} | ||
6549 | if ((tmp = configs->getstr(configs, "Tconsole", false)) != NULL) {Tconsole = tmp; D("Setting Tconsole = %s", Tconsole);} | ||
6550 | if ((tmp = configs->getstr(configs, "Tsocket", false)) != NULL) {Tsocket = tmp; D("Setting Tsocket = %s", Tsocket);} | ||
6551 | if ((tmp = configs->getstr(configs, "Ttab", false)) != NULL) {Ttab = tmp; D("Setting Ttab = %s", Ttab);} | ||
6552 | if ((tmp = configs->getstr(configs, "webRoot", false)) != NULL) {webRoot = tmp; D("Setting webRoot = %s", webRoot);} | ||
6553 | if ((tmp = configs->getstr(configs, "URL", false)) != NULL) {URL = tmp; D("Setting URL = %s", URL);} | ||
6554 | if ((vd = configs->get (configs, "seshRenew", NULL, false)) != NULL) {seshRenew = (int) *((float *) vd); D("Setting seshRenew = %d", seshRenew);} | ||
6555 | if ((vd = configs->get (configs, "idleTimeOut", NULL, false)) != NULL) {idleTimeOut = (int) *((float *) vd); D("Setting idleTimeOut = %d", idleTimeOut);} | ||
6556 | if ((vd = configs->get (configs, "seshTimeOut", NULL, false)) != NULL) {seshTimeOut = (int) *((float *) vd); D("Setting seshTimeOut = %d", seshTimeOut);} | ||
6557 | if ((vd = configs->get (configs, "newbieTimeOut", NULL, false)) != NULL) {newbieTimeOut = (int) *((float *) vd); D("Setting newbieTimeOut = %d", newbieTimeOut);} | ||
6558 | if ((tmp = configs->getstr(configs, "ToS", false)) != NULL) {ToS = tmp; D("Setting ToS = %s", ToS);} | ||
6559 | if ((tmp = configs->getstr(configs, "webIframers", false)) != NULL) {webIframers = tmp; D("Setting webIframers = %s", webIframers);} | ||
6560 | 8096 | ||
8097 | if (result) | ||
8098 | { | ||
8099 | DEBUG = configs->getint(configs, "debug"); | ||
8100 | V("Setting DEBUG = %d", DEBUG); | ||
8101 | if ((vd = configs->get (configs, "loadAverageInc", NULL, false)) != NULL) {loadAverageInc = *((float *) vd); V("Setting loadAverageInc = %f", loadAverageInc);} | ||
8102 | if ((vd = configs->get (configs, "simTimeOut", NULL, false)) != NULL) {simTimeOut = (int) *((float *) vd); V("Setting simTimeOut = %d", simTimeOut);} | ||
8103 | if ((vd = configs->get (configs, "bulkSims", NULL, false)) != NULL) {bulkSims = (int) *((float *) vd); V("Setting bulkSims = %d", bulkSims);} | ||
8104 | if ((tmp = configs->getstr(configs, "scRoot", false)) != NULL) {scRoot = tmp; V("Setting scRoot = %s", scRoot);} | ||
8105 | if ((tmp = configs->getstr(configs, "scUser", false)) != NULL) {scUser = tmp; V("Setting scUser = %s", scUser);} | ||
8106 | if ((tmp = configs->getstr(configs, "Tconsole", false)) != NULL) {Tconsole = tmp; V("Setting Tconsole = %s", Tconsole);} | ||
8107 | if ((tmp = configs->getstr(configs, "Tsocket", false)) != NULL) {Tsocket = tmp; V("Setting Tsocket = %s", Tsocket);} | ||
8108 | if ((tmp = configs->getstr(configs, "Ttab", false)) != NULL) {Ttab = tmp; V("Setting Ttab = %s", Ttab);} | ||
8109 | if ((tmp = configs->getstr(configs, "backupIARsim", false)) != NULL) {backupIARsim = tmp; V("Setting backupIARsim = %s", backupIARsim);} | ||
8110 | if ((tmp = configs->getstr(configs, "rsync", false)) != NULL) {rSync = tmp; V("Setting rsync = %s", rSync);} | ||
8111 | if ((vd = configs->getstr(configs, "rsyncPort", false)) != NULL) {rSyncPort = (int) *((float *) vd); V("Setting rsyncPort = %s", rSyncPort);} | ||
8112 | if ((tmp = configs->getstr(configs, "webRoot", false)) != NULL) {webRoot = tmp; V("Setting webRoot = %s", webRoot);} | ||
8113 | if ((tmp = configs->getstr(configs, "URL", false)) != NULL) {URL = tmp; V("Setting URL = %s", URL);} | ||
8114 | if ((vd = configs->get (configs, "seshRenew", NULL, false)) != NULL) {seshRenew = (int) *((float *) vd); V("Setting seshRenew = %d", seshRenew);} | ||
8115 | if ((vd = configs->get (configs, "idleTimeOut", NULL, false)) != NULL) {idleTimeOut = (int) *((float *) vd); V("Setting idleTimeOut = %d", idleTimeOut);} | ||
8116 | if ((vd = configs->get (configs, "seshTimeOut", NULL, false)) != NULL) {seshTimeOut = (int) *((float *) vd); V("Setting seshTimeOut = %d", seshTimeOut);} | ||
8117 | if ((vd = configs->get (configs, "newbieTimeOut", NULL, false)) != NULL) {newbieTimeOut = (int) *((float *) vd); V("Setting newbieTimeOut = %d", newbieTimeOut);} | ||
8118 | if ((tmp = configs->getstr(configs, "ToS", false)) != NULL) {ToS = tmp; V("Setting ToS = %s", ToS);} | ||
8119 | if ((tmp = configs->getstr(configs, "webIframers", false)) != NULL) {webIframers = tmp; V("Setting webIframers = %s", webIframers);} | ||
8120 | } | ||
8121 | else | ||
8122 | { | ||
8123 | C("No sledjChisl.conf.lua found!"); | ||
8124 | goto finished; | ||
8125 | } | ||
6561 | 8126 | ||
8127 | snprintf(toybuf, sizeof(toybuf), "%s/current/bin", scRoot); | ||
8128 | t1 = realpath(toybuf, NULL); | ||
8129 | if ((NULL != t0) && (strcmp(t0, t1))) | ||
8130 | C("scRoot moved unexpectedly, check your sledjChisl.conf.lua files! %s != %s", t0, t1); | ||
8131 | free(t1); | ||
8132 | free(t0); | ||
8133 | |||
8134 | |||
8135 | /* TODO - stuff that used to be in InstallItAll.sh | ||
8136 | Grab old database details from the config/config.ini file. | ||
8137 | |||
8138 | Check the external requirements are met. | ||
8139 | sudo apt install mariadb-server libmariadbclient-dev tmux mono-complete mono-devel nunit uuid-runtime uuid-dev libapache2-mod-fcgid libssl1.0-dev spawn-fcgi | ||
8140 | sudo apt install nant | ||
8141 | |||
8142 | Make sure the database is running. | ||
8143 | sudo /etc/init.d/mysql restart | ||
8144 | |||
8145 | Setup the database stuff. | ||
8146 | create database if not exists $MYSQL_DB; | ||
8147 | create user if not exists '$MYSQL_USER' identified by '$MYSQL_PASSWORD'; | ||
8148 | create user if not exists '$MYSQL_USER'@localhost identified by '$MYSQL_PASSWORD'; | ||
8149 | grant all on $MYSQL_DB.* to '$MYSQL_USER'; | ||
8150 | grant all on $MYSQL_DB.* to '$MYSQL_USER'@localhost; | ||
8151 | FLUSH PRIVILEGES; | ||
8152 | |||
8153 | Deal with the system user. | ||
8154 | sudo adduser --system --shell /bin/bash --group ${OS_USER} | ||
8155 | sudo addgroup ${USER} ${OS_USER} | ||
8156 | |||
8157 | Copy the tmux config to user's homes. | ||
8158 | |||
8159 | Copy this version of opensim-SC to /opt/opensim-SC, and make the current symlink to it. | ||
8160 | |||
8161 | Copy correct config/config.ini to there. | ||
8162 | Put new database credentials in config/config.ini | ||
8163 | |||
8164 | Build the OpenSim. | ||
8165 | */ | ||
8166 | |||
8167 | |||
8168 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
8169 | // Sort out directories. | ||
8170 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
6562 | // Use a FHS compatible setup - | 8171 | // Use a FHS compatible setup - |
6563 | if (strcmp("/", scRoot) == 0) | 8172 | if (strcmp("/", scRoot) == 0) |
6564 | { | 8173 | { |
@@ -6570,6 +8179,7 @@ jit library is loaded or the JIT compiler will not be activated. | |||
6570 | scCache = "/var/cache/opensim_SC"; | 8179 | scCache = "/var/cache/opensim_SC"; |
6571 | scData = "/var/lib/opensim_SC"; | 8180 | scData = "/var/lib/opensim_SC"; |
6572 | scLog = "/var/log/opensim_SC"; | 8181 | scLog = "/var/log/opensim_SC"; |
8182 | scTemp = "/tmp"; | ||
6573 | } | 8183 | } |
6574 | else if (strcmp("/usr/local", scRoot) == 0) | 8184 | else if (strcmp("/usr/local", scRoot) == 0) |
6575 | { | 8185 | { |
@@ -6581,6 +8191,7 @@ jit library is loaded or the JIT compiler will not be activated. | |||
6581 | scCache = "/var/local/cache/opensim_SC"; | 8191 | scCache = "/var/local/cache/opensim_SC"; |
6582 | scData = "/var/local/lib/opensim_SC"; | 8192 | scData = "/var/local/lib/opensim_SC"; |
6583 | scLog = "/var/local/log/opensim_SC"; | 8193 | scLog = "/var/local/log/opensim_SC"; |
8194 | scTemp = "/tmp"; | ||
6584 | } | 8195 | } |
6585 | else // A place for everything to live, like /opt/opensim_SC | 8196 | else // A place for everything to live, like /opt/opensim_SC |
6586 | { | 8197 | { |
@@ -6595,6 +8206,7 @@ jit library is loaded or the JIT compiler will not be activated. | |||
6595 | // Don't want it in the path. | 8206 | // Don't want it in the path. |
6596 | // Could put the .dlls into lib. Most of the rest is config files or common assets. | 8207 | // Could put the .dlls into lib. Most of the rest is config files or common assets. |
6597 | // Or just slowly migrate opensim stuff to FHS. | 8208 | // Or just slowly migrate opensim stuff to FHS. |
8209 | // TODO - free these. Though valgrind isn't bitching about them. | ||
6598 | scBin = xmprintf("%s%s/bin", slsh, scRoot); | 8210 | scBin = xmprintf("%s%s/bin", slsh, scRoot); |
6599 | scEtc = xmprintf("%s%s/etc", slsh, scRoot); | 8211 | scEtc = xmprintf("%s%s/etc", slsh, scRoot); |
6600 | scLib = xmprintf("%s%s/lib", slsh, scRoot); | 8212 | scLib = xmprintf("%s%s/lib", slsh, scRoot); |
@@ -6603,50 +8215,125 @@ jit library is loaded or the JIT compiler will not be activated. | |||
6603 | scCache = xmprintf("%s%s/var/cache", slsh, scRoot); | 8215 | scCache = xmprintf("%s%s/var/cache", slsh, scRoot); |
6604 | scData = xmprintf("%s%s/var/lib", slsh, scRoot); | 8216 | scData = xmprintf("%s%s/var/lib", slsh, scRoot); |
6605 | scLog = xmprintf("%s%s/var/log", slsh, scRoot); | 8217 | scLog = xmprintf("%s%s/var/log", slsh, scRoot); |
8218 | scTemp = xmprintf("%s%s/tmp", slsh, scRoot); | ||
6606 | } | 8219 | } |
6607 | 8220 | ||
8221 | // A bit chicken and egg here. | ||
8222 | // We need to be the correct user to set the correct perms on the scRun directory | ||
8223 | // But we wont be the correct user until we run tmux as that user. | ||
8224 | // The path to sledjchisl.socket needs to be readable by the www-data group. So the FCGI socket will work. | ||
8225 | // AND it needs to be group sticky on opensimsc group. So the tmux socket will work. | ||
8226 | // Put both in scRun, and make it other readable? | ||
8227 | // TODO - No idea of the SECURITY HOLE that might cause. | ||
8228 | if(!qfile_exist(scRun)) | ||
8229 | { | ||
8230 | W("Creating the proper %s directory.", scRun); | ||
8231 | if (shellMeFail("sudo -Hu %s mkdir -p %s", scUser, scRun)) | ||
8232 | { | ||
8233 | C("Can't make the %s directory.", scRun); | ||
8234 | goto finished; | ||
8235 | } | ||
8236 | else | ||
8237 | { | ||
8238 | if (shellMeFail("sudo -Hu %s chmod 2755 %s", scUser, scRun)) | ||
8239 | { | ||
8240 | C("Can't set proper permissions for the %s directory.", scRun); | ||
8241 | goto finished; | ||
8242 | } | ||
8243 | } | ||
8244 | } | ||
6608 | 8245 | ||
6609 | // TODO - still a bit chicken and egg here about the tmux socket and reading configs from scEtc /.sledjChisl.conf.lua | ||
6610 | if (!isWeb) | 8246 | if (!isWeb) |
6611 | { | 8247 | { |
6612 | I("Outputting to a terminal, not a web server."); | 8248 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
8249 | // Figure out what and where we are. | ||
8250 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
8251 | V("Outputting to a terminal, not a web server."); | ||
6613 | // Check if we are already running inside the proper tmux server. | 8252 | // Check if we are already running inside the proper tmux server. |
6614 | char *eTMUX = getenv("TMUX"); | 8253 | char *eTMUX = getenv("TMUX"); |
6615 | memset(toybuf, 0, sizeof(toybuf)); | 8254 | |
6616 | snprintf(toybuf, sizeof(toybuf), "%s/%s", scRun, Tsocket); | 8255 | snprintf(toybuf, sizeof(toybuf), "%s/%s", scRun, Tsocket); |
6617 | if (((eTMUX) && (0 == strncmp(toybuf, eTMUX, strlen(toybuf))))) | 8256 | if (((eTMUX) && (0 == strncmp(toybuf, eTMUX, strlen(toybuf))))) |
6618 | { | 8257 | { |
6619 | I("Running inside the proper tmux server. %s", eTMUX); | 8258 | V("Running inside the proper tmux server. %s", eTMUX); |
6620 | isTmux = TRUE; | 8259 | isTmux = TRUE; |
6621 | } | 8260 | } |
6622 | else | 8261 | else |
6623 | I("Not running inside the proper tmux server, starting it. %s == %s", eTMUX, toybuf); | 8262 | V("Not running inside the proper tmux server. %s == %s", eTMUX, toybuf); |
6624 | } | 8263 | } |
6625 | 8264 | ||
8265 | // Note this block of code assumes we are running inside current/bin. Which RunIt.sh does for us. Earlier code also tries to ensure this. | ||
8266 | if (!isTmux) | ||
8267 | { // Let's see if the proper tmux server is even running. | ||
8268 | i = shellMe("%s %s/%s -q list-sessions 2>/dev/null | grep -q %s:", Tcmd, scRun, Tsocket, Tconsole); | ||
8269 | if (WIFEXITED(i)) | ||
8270 | { | ||
8271 | if (0 != WEXITSTATUS(i)) // No such session, create it. | ||
8272 | { | ||
8273 | char *pre = xmprintf("sudo -Hu %s ", scUser); | ||
8274 | |||
8275 | memset(toybuf, 0, sizeof(toybuf)); | ||
8276 | if (strcmp(pw->pw_name, scUser) == 0) | ||
8277 | { | ||
8278 | I("Not running inside the proper tmux server, starting it."); | ||
8279 | pre = xmprintf(""); | ||
8280 | } | ||
8281 | else | ||
8282 | { | ||
8283 | I("Not running inside the proper tmux server, starting it with sudo."); | ||
8284 | // 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. | ||
8285 | // After the session is created, we rely on the scRun directory to be group sticky, so that anyone in the opensim group can attach to the tmux socket. | ||
8286 | pre = xmprintf("sudo -Hu %s ", scUser); | ||
8287 | } | ||
8288 | snprintf(toybuf, sizeof(toybuf), | ||
8289 | "%s %s %s/%s set-option -g default-command 'export PATH=%s:$PATH; bash'\\; " | ||
8290 | // Set x,y to huge values, to work around an OpenSim bug. | ||
8291 | "new-session -d -x 256 -y 420 -s %s -n '%s' bash -c 'export PATH=%s:$PATH; /usr/bin/valgrind --leak-check=full ./sledjchisl; cd %s/current/bin; bash' \\; " | ||
8292 | "split-window -vp 50 -d -t '%s:0.0' \\; " | ||
8293 | "split-window -hp 50 -d -t '%s:0.1' \\; " | ||
8294 | "split-window -hp 50 -d -t '%s:0.0' " | ||
8295 | // TODO - remove this once we handle the fcgi stuff ourselves. | ||
8296 | // Make the FCGI socket world read / write, coz we can't set it to group www-data, since -G only works for root, and we ain't root. | ||
8297 | // TODO - BIG SECURITY HOLE??? | ||
8298 | "bash -c 'export PATH=%s:$PATH; " | ||
8299 | "spawn-fcgi -n -u %s -s %s/sledjchisl.socket -M 0666 -G www-data -- /usr/bin/valgrind --leak-check=full sledjchisl; " | ||
8300 | "cd %s/current/bin; bash'" | ||
8301 | , | ||
8302 | pre, Tcmd, scRun, Tsocket, scBin, Tconsole, Ttab, scBin, scRoot, Tconsole, Tconsole, Tconsole, scBin, scUser, scRun, scBin, scRoot); | ||
8303 | free(pre); | ||
8304 | if (shellMeFail(toybuf)) | ||
8305 | E("tmux new-session command failed! %s", toybuf); | ||
8306 | else | ||
8307 | { | ||
8308 | if (shellMeFail("%s %s/%s select-pane -t 1 -T 'SledjChisl' \\; select-pane -t @0.%%3 -T 'FCGI web server'", Tcmd, scRun, Tsocket)) | ||
8309 | E("tmux select-pane command failed!"); | ||
8310 | } | ||
8311 | // toybox argument parsing is half working. | ||
8312 | if (!FLAG(q)) | ||
8313 | { | ||
8314 | V("Joining the session."); | ||
8315 | // Join the session. | ||
8316 | if (shellMeFail("%s %s/%s select-window -t '%s' \\; attach-session -t '%s'", Tcmd, scRun, Tsocket, Tconsole, Tconsole)) | ||
8317 | E("tmux attach-session command failed! %s", toybuf); | ||
8318 | } | ||
8319 | else | ||
8320 | V("NOT joining the session."); | ||
8321 | goto finished; | ||
8322 | } | ||
8323 | } | ||
8324 | else | ||
8325 | E("tmux list-sessions command failed! %s", toybuf); | ||
8326 | } | ||
8327 | |||
8328 | |||
8329 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
8330 | // Other start up stuff. | ||
8331 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
8332 | getSims(); | ||
6626 | if (isTmux || isWeb) | 8333 | if (isTmux || isWeb) |
6627 | { | 8334 | { |
6628 | char *d; | 8335 | char *d; |
6629 | 8336 | ||
6630 | // Doing this here coz at this point we should be the correct user. | ||
6631 | if ((! qfile_exist(scBin)) && (! qfile_mkdir(scBin, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP, true))) C("Unable to create path %s", scBin); | ||
6632 | if ((! qfile_exist(scEtc)) && (! qfile_mkdir(scEtc, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP, true))) C("Unable to create path %s", scEtc); | ||
6633 | if ((! qfile_exist(scLib)) && (! qfile_mkdir(scLib, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP, true))) C("Unable to create path %s", scLib); | ||
6634 | if ((! qfile_exist(scRun)) && (! qfile_mkdir(scRun, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | S_ISGID, true))) C("Unable to create path %s", scRun); | ||
6635 | if ((! qfile_exist(scBackup)) && (! qfile_mkdir(scBackup, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP, true))) C("Unable to create path %s", scBackup); | ||
6636 | // TODO - the path to scCache/sledjchisl.socket needs to be readable by the www-data group. So the FCGI socket will work. | ||
6637 | // AND it needs to be group sticky on opensimsc group. So the tmux socket will work. | ||
6638 | // So currently scCache is www-data readable, and scRun is group sticky. | ||
6639 | if ((! qfile_exist(scCache)) && (! qfile_mkdir(scCache, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP, true))) C("Unable to create path %s", scCache); | ||
6640 | if ((! qfile_exist(scData)) && (! qfile_mkdir(scData, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP, true))) C("Unable to create path %s", scData); | ||
6641 | if ((! qfile_exist(scLog)) && (! qfile_mkdir(scLog, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP, true))) C("Unable to create path %s", scLog); | ||
6642 | tmp = xmprintf("%s/sessions", scCache); | ||
6643 | if ((! qfile_exist(tmp)) && (! qfile_mkdir(tmp, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP, true))) C("Unable to create path %s", tmp); | ||
6644 | free(tmp); | ||
6645 | tmp = xmprintf("%s/users", scData); | ||
6646 | if ((! qfile_exist(tmp)) && (! qfile_mkdir(tmp, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP, true))) C("Unable to create path %s", tmp); | ||
6647 | free(tmp); | ||
6648 | |||
6649 | |||
6650 | mimeTypes = qhashtbl(0, 0); | 8337 | mimeTypes = qhashtbl(0, 0); |
6651 | mimeTypes->putstr(mimeTypes, "gz", "application/gzip"); | 8338 | mimeTypes->putstr(mimeTypes, "gz", "application/gzip"); |
6652 | mimeTypes->putstr(mimeTypes, "js", "application/javascript"); | 8339 | mimeTypes->putstr(mimeTypes, "js", "application/javascript"); |
@@ -6674,13 +8361,15 @@ jit library is loaded or the JIT compiler will not be activated. | |||
6674 | // mimeTypes->putstr(mimeTypes, "markdown", "text/markdown"); | 8361 | // mimeTypes->putstr(mimeTypes, "markdown", "text/markdown"); |
6675 | mimeTypes->putstr(mimeTypes, "txt", "text/plain"); | 8362 | mimeTypes->putstr(mimeTypes, "txt", "text/plain"); |
6676 | 8363 | ||
6677 | memset(toybuf, 0, sizeof(toybuf)); | ||
6678 | snprintf(toybuf, sizeof(toybuf), "%s/config/config.ini", scRoot); | ||
6679 | 8364 | ||
8365 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
8366 | // Connect to database. | ||
8367 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
6680 | // TODO - it looks like OpenSim invented their own half arsed backwards INI file include system. | 8368 | // TODO - it looks like OpenSim invented their own half arsed backwards INI file include system. |
6681 | // I doubt qLibc supports it, like it supports what seems to be the standard include system. | 8369 | // I doubt qLibc supports it, like it supports what seems to be the standard include system. |
6682 | // Not sure if we need to worry about it just yet. | 8370 | // Not sure if we need to worry about it just yet. |
6683 | // TODO - this leaks memory, but it's a bug in qLibc. Send the bug fix upstream. | 8371 | // TODO - this leaks memory, but it's a bug in qLibc. Send the bug fix upstream. |
8372 | snprintf(toybuf, sizeof(toybuf), "%s/config/config.ini", scRoot); | ||
6684 | qlisttbl_t *qconfig = qconfig_parse_file(NULL, toybuf, '='); | 8373 | qlisttbl_t *qconfig = qconfig_parse_file(NULL, toybuf, '='); |
6685 | if (NULL == qconfig) | 8374 | if (NULL == qconfig) |
6686 | { | 8375 | { |
@@ -6745,8 +8434,262 @@ jit library is loaded or the JIT compiler will not be activated. | |||
6745 | } | 8434 | } |
6746 | 8435 | ||
6747 | 8436 | ||
6748 | if (isWeb) | 8437 | if (!isWeb) |
8438 | { | ||
8439 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
8440 | // Start of OpenSim wrangling section. | ||
8441 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
8442 | struct sysinfo info; | ||
8443 | // TODO - See https://stackoverflow.com/questions/2693948/how-do-i-retrieve-the-number-of-processors-on-c-linux, there are more portable ways, this seems to be GNU specific. | ||
8444 | int cpus = (int) sysconf(_SC_NPROCESSORS_CONF), cpusOnline = (int) sysconf(_SC_NPROCESSORS_ONLN); | ||
8445 | |||
8446 | ourSims->doIt = 1; | ||
8447 | if (0 == bulkSims) | ||
8448 | bulkSims = (cpusOnline / 3) - 1; | ||
8449 | sysinfo(&info); | ||
8450 | ourSims->la = info.loads[0]/65536.0; | ||
8451 | I("There are %i CPUs, %i of them are online. Doing %i sims at once. LoadAverage = %f", cpus, cpusOnline, bulkSims, ourSims->la); | ||
8452 | |||
8453 | if (0 == toys.optc) | ||
8454 | ; | ||
8455 | else if (strcmp(toys.optargs[0], "backup") == 0) | ||
8456 | currentMode = BACKUP; | ||
8457 | else if (strcmp(toys.optargs[0], "gitar") == 0) | ||
8458 | currentMode = GITAR; | ||
8459 | else if (strcmp(toys.optargs[0], "restart") == 0) | ||
8460 | currentMode = RESTART; | ||
8461 | else if (strcmp(toys.optargs[0], "start") == 0) | ||
8462 | currentMode = START; | ||
8463 | else if (strcmp(toys.optargs[0], "status") == 0) | ||
8464 | currentMode = STATUS; | ||
8465 | else if (strcmp(toys.optargs[0], "stop") == 0) | ||
8466 | currentMode = STOP; | ||
8467 | if (2 == toys.optc) | ||
8468 | ourSims->target = xstrdup(toys.optargs[1]); | ||
8469 | else if (3 == toys.optc) | ||
8470 | ourSims->target = xmprintf("%s %s", toys.optargs[1], toys.optargs[2]); | ||
8471 | else | ||
8472 | ourSims->target = NULL; | ||
8473 | //V("Doing %s for %s '%s' %s %s", modeStrings[currentMode], FLAG(m) ? "member" : "", ourSims->target, FLAG(q) ? "quiet" : "", FLAG(a) ? "all" : ""); | ||
8474 | |||
8475 | // Start ROBUST or join the tmux session, or just figure out where the sims are running in tmux. | ||
8476 | if ((START == currentMode) && !checkSimIsRunning("ROBUST")) | ||
8477 | { | ||
8478 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
8479 | // Sort out directories, part 2 | ||
8480 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
8481 | |||
8482 | // Doing this here coz at this point we should be the correct user, and we only want to do this during initial startup. | ||
8483 | /* From man 7 inode - | ||
8484 | S_ISUID 04000 set-user-ID bit | ||
8485 | S_ISGID 02000 set-group-ID bit (see below) | ||
8486 | S_ISVTX 01000 sticky bit (see below) | ||
8487 | |||
8488 | S_IRWXU 00700 owner has read, write, and execute permission | ||
8489 | S_IRUSR 00400 owner has read permission | ||
8490 | S_IWUSR 00200 owner has write permission | ||
8491 | S_IXUSR 00100 owner has execute permission | ||
8492 | |||
8493 | S_IRWXG 00070 group has read, write, and execute permission | ||
8494 | S_IRGRP 00040 group has read permission | ||
8495 | S_IWGRP 00020 group has write permission | ||
8496 | S_IXGRP 00010 group has execute permission | ||
8497 | |||
8498 | S_IRWXO 00007 others (not in group) have read, write, and execute permission | ||
8499 | S_IROTH 00004 others have read permission | ||
8500 | S_IWOTH 00002 others have write permission | ||
8501 | S_IXOTH 00001 others have execute permission | ||
8502 | |||
8503 | The set-group-ID bit (S_ISGID) has several special uses. For a directory, it indicates that BSD semantics is to be used for that directory: files created there inherit their group | ||
8504 | ID from the directory, not from the effective group ID of the creating process, and directories created there will also get the S_ISGID bit set. For a file that does not have the | ||
8505 | group execution bit (S_IXGRP) set, the set-group-ID bit indicates mandatory file/record locking. | ||
8506 | |||
8507 | The sticky bit (S_ISVTX) on a directory means that a file in that directory can be renamed or deleted only by the owner of the file, by the owner of the directory, | ||
8508 | and by a privileged process | ||
8509 | */ | ||
8510 | V("Making directories in %s.", scRoot); | ||
8511 | if ((! qfile_exist(scBin)) && (! qfile_mkdir(scBin, S_IRWXU | S_IRGRP | S_IXGRP, true))) C("Unable to create path %s", scBin); | ||
8512 | if ((! qfile_exist(scEtc)) && (! qfile_mkdir(scEtc, S_IRWXU | S_IRGRP | S_IXGRP, true))) C("Unable to create path %s", scEtc); | ||
8513 | if ((! qfile_exist(scLib)) && (! qfile_mkdir(scLib, S_IRWXU | S_IRGRP | S_IXGRP, true))) C("Unable to create path %s", scLib); | ||
8514 | if ((! qfile_exist(scBackup)) && (! qfile_mkdir(scBackup, S_IRWXU | S_IRGRP | S_IXGRP, true))) C("Unable to create path %s", scBackup); | ||
8515 | if ((! qfile_exist(scCache)) && (! qfile_mkdir(scCache, S_IRWXU | S_IRGRP | S_IXGRP, true))) C("Unable to create path %s", scCache); | ||
8516 | if ((! qfile_exist(scData)) && (! qfile_mkdir(scData, S_IRWXU | S_IRGRP | S_IXGRP, true))) C("Unable to create path %s", scData); | ||
8517 | if ((! qfile_exist(scLog)) && (! qfile_mkdir(scLog, S_IRWXU | S_IRGRP | S_IXGRP, true))) C("Unable to create path %s", scLog); | ||
8518 | if ((! qfile_exist(scTemp)) && (! qfile_mkdir(scTemp, S_IRWXU | S_IRGRP | S_IXGRP, true))) C("Unable to create path %s", scTemp); | ||
8519 | tmp = xmprintf("%s/sessions", scCache); | ||
8520 | if ((! qfile_exist(tmp)) && (! qfile_mkdir(tmp, S_IRWXU | S_IRGRP | S_IXGRP, true))) C("Unable to create path %s", tmp); | ||
8521 | free(tmp); | ||
8522 | tmp = xmprintf("%s/users", scData); | ||
8523 | if ((! qfile_exist(tmp)) && (! qfile_mkdir(tmp, S_IRWXU | S_IRGRP | S_IXGRP, true))) C("Unable to create path %s", tmp); | ||
8524 | free(tmp); | ||
8525 | tmp = xmprintf("%s/db", scData); | ||
8526 | if ((! qfile_exist(tmp)) && (! qfile_mkdir(tmp, S_IRWXU | S_IRGRP | S_IXGRP, true))) C("Unable to create path %s", tmp); | ||
8527 | free(tmp); | ||
8528 | |||
8529 | /* TODO - tighten up security. | ||
8530 | Make sure correct permissions are set everywhere. | ||
8531 | sudo chown -R ${OS_USER}:${OS_USER} ${OS_PATH} | ||
8532 | |||
8533 | Create the /opt/opensim-SC directory structure. | ||
8534 | AssetFiles/data Think OpenSim creates all the sub directories itself? | ||
8535 | AssetFiles/tmp/spool Think OpenSim creates all the sub directories itself? | ||
8536 | config/config.ini (move that etc/config.ini later) | ||
8537 | . var/backups Copy examples/var/backups/*.IAR files, which are the newbie starter inventories. | ||
8538 | . var/cache Think OpenSim creates all the sub directories itself? | ||
8539 | . var/run HAS to be setup correctly BEFORE we try to start up tmux. | ||
8540 | web // Fill it with default web stuff from current -> example.. | ||
8541 | */ | ||
8542 | I("Securing directories and files in %s. This might take awhile.", scRoot); | ||
8543 | if (shellMeFail("chmod u=rw,go= %s/config/*.ini", scRoot)) C("Can't set proper permissions for %s/config*.ini", scRoot); | ||
8544 | if (shellMeFail("chmod u=rw,go= %s/config/ROBUST/*.ini", scRoot)) C("Can't set proper permissions for %s/config/ROBUST/*.ini", scRoot); | ||
8545 | if (shellMeFail("chmod u=rw,go= %s/.sledjChisl.conf.lua", scEtc)) C("Can't set proper permissions for %s/.sledjChisl.conf.lua", scEtc); | ||
8546 | if (shellMeFail("chmod ug+rw %s/config", scRoot)) C("Can't set proper permissions for %s/config", scRoot); | ||
8547 | if (shellMeFail("chmod g+s %s/config", scRoot)) C("Can't set proper permissions for %s/config", scRoot); | ||
8548 | if (shellMeFail("chmod u=rw,go= %s/config/*.ini", scRoot)) C("Can't set proper permissions for %s/config/*.ini", scRoot); | ||
8549 | if (shellMeFail("chmod u=rw,go= %s/config/ROBUST/*.ini", scRoot)) C("Can't set proper permissions for %s/configROBUST/*.ini", scRoot); | ||
8550 | |||
8551 | if (shellMeFail("chmod ug=rwx,o= %s/AssetFiles", scRoot)) C("Can't set proper permissions for %s/AssetFiles", scRoot); | ||
8552 | if (shellMeFail("chmod -fR ug=rw,o=,a+X %s", scBackup)) C("Can't set proper permissions for %s", scBackup); | ||
8553 | if (shellMeFail("chmod -fR a=r,ug=rw,a+X %s", scBin)) C("Can't set proper permissions for %s", scBin); | ||
8554 | if (shellMeFail("chmod -fR ug=rw,o=,a+X %s", scCache)) C("Can't set proper permissions for %s", scCache); | ||
8555 | if (shellMeFail("chmod ug=rwx,o= %s", scCache)) C("Can't set proper permissions for %s", scCache); | ||
8556 | if (shellMeFail("chmod -fR a=r,ug=rw,a+X %s", scData)) C("Can't set proper permissions for %s", scData); | ||
8557 | if (shellMeFail("chmod -fR a=r,ug=rw,a+X %s", scEtc)) C("Can't set proper permissions for %s", scEtc); | ||
8558 | if (shellMeFail("chmod u=rw,go= %s/.sledjChisl.conf.lua", scEtc)) C("Can't set proper permissions for %s/.sledjChisl.conf.lua", scEtc); | ||
8559 | if (shellMeFail("chmod g+s %s", scEtc)) C("Can't set proper permissions for %s", scEtc); | ||
8560 | if (shellMeFail("chmod a+x %s/*.shini", scEtc)) C("Can't set proper permissions for %s/*.shini", scEtc); | ||
8561 | if (shellMeFail("chmod -fR a=r,ug=rw,a+X %s", scLib)) C("Can't set proper permissions for %s", scLib); | ||
8562 | if (shellMeFail("chmod -fR a=r,ug=rw,a+X %s", scLog)) C("Can't set proper permissions for %s", scLog); | ||
8563 | if (shellMeFail("chmod -fR a=r,ug=rw,a+X %s", scTemp)) C("Can't set proper permissions for %s", scTemp); | ||
8564 | |||
8565 | // if (shellMeFail("chmod -R a+x %s/current/*.sh", scRoot)) C("Can't set proper permissions for %s/current/*.sh", scRoot); | ||
8566 | // if (shellMeFail("chmod -R a+x %s/current/scripts/*.sh", scRoot)) C("Can't set proper permissions for %s/current/scripts/*.sh", scRoot); | ||
8567 | // if (shellMeFail("chmod -R a+x %s/current/scripts/install/*.sh", scRoot)) C("Can't set proper permissions for %s/current/scripts/install/*.sh", scRoot); | ||
8568 | // if (shellMeFail("chmod a+x %s/current/scripts/show-console", scRoot)) C("Can't set proper permissions for %s/current/scripts/show-console", scRoot); | ||
8569 | // if (shellMeFail("chmod a+x %s/current/scripts/start-sim", scRoot)) C("Can't set proper permissions for %s/current/scripts/start-sim", scRoot); | ||
8570 | |||
8571 | char *newPath = xmprintf("%s/current/bin/sledjchisl", scRoot); | ||
8572 | tmp = xmprintf("%s/sledjchisl", scBin); | ||
8573 | V("Symlinking %s to %s", newPath, tmp); | ||
8574 | if (qfile_exist(tmp)) | ||
8575 | { | ||
8576 | if (shellMeFail("rm %s", tmp)) | ||
8577 | E("rm command failed!"); | ||
8578 | } | ||
8579 | if (0 != symlink(newPath, tmp)) | ||
8580 | perror_msg("Symlinking %s to %s", newPath, tmp); | ||
8581 | free(tmp); | ||
8582 | free(newPath); | ||
8583 | |||
8584 | char *c = xmprintf("cd %s/current/bin", scRoot); | ||
8585 | I("ROBUST is starting up."); | ||
8586 | sendTmuxCmd("@0.%1", c); | ||
8587 | free(c); | ||
8588 | c = xmprintf("mono Robust.exe -inidirectory=%s/config/ROBUST", scRoot); | ||
8589 | sendTmuxCmd("@0.%1", c); | ||
8590 | free(c); | ||
8591 | doTmuxCmd("select-pane -t @0.%%1 -T 'ROBUST'", Tcmd, scRun, Tsocket); | ||
8592 | |||
8593 | // Create all the tmux windows, panes, and temporary .ini files coz OpenSim sucketh. | ||
8594 | forEachSim("Prepping", prepSims, NULL); | ||
8595 | waitTmuxText("@0.%1", "INITIALIZATION COMPLETE FOR ROBUST"); | ||
8596 | I("ROBUST is done starting up."); | ||
8597 | } | ||
8598 | else if ((0 == toys.optc) && (checkSimIsRunning("ROBUST"))) | ||
8599 | { | ||
8600 | // Join the session. | ||
8601 | doTmuxCmd("select-window -t '%s' \\; attach-session -t '%s'", Tconsole, Tconsole); | ||
8602 | goto finished; | ||
8603 | } | ||
8604 | else // Find out where the sims are in tmux. | ||
8605 | forEachSim(NULL, findSimsTmux, NULL); | ||
8606 | |||
8607 | if (FLAG(m)) | ||
8608 | { | ||
8609 | // Do stuff with the members. | ||
8610 | ourSims->doIt = 1; | ||
8611 | // Find the correct window to do backups in. | ||
8612 | forEachSim(NULL, findWindow, NULL); | ||
8613 | forEachMember("Doing", doSimsThing, NULL); | ||
8614 | } | ||
8615 | else | ||
8616 | { | ||
8617 | // TODO if stopping all the sims, I'd want to do them in reverse order. | ||
8618 | if ((STOP == currentMode) && (NULL == ourSims->target)) | ||
8619 | I("Stopping all the sims."); | ||
8620 | // Do stuff with the sims. | ||
8621 | ourSims->doIt = 1; | ||
8622 | forEachSim("Doing", doSimsThing, NULL); | ||
8623 | } | ||
8624 | |||
8625 | if ((STOP == currentMode) && (NULL == ourSims->target)) | ||
8626 | { | ||
8627 | // Stop all the sims. | ||
8628 | ourSims->doIt = 1; | ||
8629 | forEachSim(NULL, stopSims, NULL); | ||
8630 | |||
8631 | if (ourSims->doIt) | ||
8632 | { | ||
8633 | if (checkSimIsRunning("ROBUST")) | ||
8634 | { | ||
8635 | I("Stopping ROBUST."); | ||
8636 | sendTmuxCmd("@0.%1", "quit"); | ||
8637 | while (checkSimIsRunning("ROBUST")) | ||
8638 | usleep(100000); | ||
8639 | usleep(100000); | ||
8640 | sendTmuxCmd("@0.%1", "exit"); | ||
8641 | usleep(100000); | ||
8642 | sendTmuxCmd("@0.%1", "exit"); | ||
8643 | I("ROBUST has stopped in tmux ID @0.%%1."); | ||
8644 | } | ||
8645 | |||
8646 | if (FLAG(a)) | ||
8647 | { | ||
8648 | I("Closing all the other windows."); | ||
8649 | // First figure out what panes and windows are left. | ||
8650 | shellMeFail("echo 'IDs={' >%s/IDs_ALL.lua", scTemp); | ||
8651 | doTmuxCmd("list-panes -s -F '\"#{window_id}.#{pane_id}\",' >> %s/IDs_ALL.lua", scTemp); | ||
8652 | shellMeFail("echo '}\nreturn IDs' >>%s/IDs_ALL.lua", scTemp); | ||
8653 | snprintf(toybuf, sizeof(toybuf), "%s/IDs_ALL.lua", scTemp); | ||
8654 | qtreetbl_t *IDs = Lua2tree(toybuf, "IDs"); | ||
8655 | |||
8656 | if (shellMeFail("rm -fr %s/*", scTemp)) | ||
8657 | E("rm command failed! %s/*", scTemp); | ||
8658 | |||
8659 | qtreetbl_obj_t obj0; | ||
8660 | memset((void*)&obj0, 0, sizeof(obj0)); | ||
8661 | IDs->lock(IDs); | ||
8662 | while(IDs->getnext(IDs, &obj0, false) == true) | ||
8663 | { | ||
8664 | qLua *q0 = obj0.data; | ||
8665 | |||
8666 | if ('0' != q0->v.s[1]) | ||
8667 | doTmuxCmd("kill-pane -t %s", q0->v.s); | ||
8668 | } | ||
8669 | IDs->unlock(IDs); | ||
8670 | |||
8671 | I("Closing the original window."); | ||
8672 | memset((void*)&obj0, 0, sizeof(obj0)); | ||
8673 | IDs->lock(IDs); | ||
8674 | while(IDs->getnext(IDs, &obj0, false) == true) | ||
8675 | { | ||
8676 | qLua *q0 = obj0.data; | ||
8677 | |||
8678 | if ('0' == q0->v.s[1]) | ||
8679 | doTmuxCmd("kill-pane -t %s", q0->v.s); | ||
8680 | } | ||
8681 | IDs->unlock(IDs); | ||
8682 | |||
8683 | freeLuaTree(IDs); | ||
8684 | } | ||
8685 | } | ||
8686 | } | ||
8687 | } | ||
8688 | else | ||
6749 | { | 8689 | { |
8690 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
8691 | // Start of web wrangling section. | ||
8692 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
6750 | char **initialEnv = environ; | 8693 | char **initialEnv = environ; |
6751 | char *tmp0, *tmp1; | 8694 | char *tmp0, *tmp1; |
6752 | int count = 0, entries, bytes; | 8695 | int count = 0, entries, bytes; |
@@ -6767,6 +8710,9 @@ jit library is loaded or the JIT compiler will not be activated. | |||
6767 | } | 8710 | } |
6768 | printEnv(environ); | 8711 | printEnv(environ); |
6769 | 8712 | ||
8713 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
8714 | // Web processing loop. | ||
8715 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
6770 | /* | 8716 | /* |
6771 | ? https://stackoverflow.com/questions/30707792/how-to-disable-buffering-with-apache2-and-mod-proxy-fcgi | 8717 | ? https://stackoverflow.com/questions/30707792/how-to-disable-buffering-with-apache2-and-mod-proxy-fcgi |
6772 | https://z-issue.com/wp/apache-2-4-the-event-mpm-php-via-mod_proxy_fcgi-and-php-fpm-with-vhosts/ | 8718 | https://z-issue.com/wp/apache-2-4-the-event-mpm-php-via-mod_proxy_fcgi-and-php-fpm-with-vhosts/ |
@@ -6893,7 +8839,7 @@ t("BODY"); | |||
6893 | free(Body); | 8839 | free(Body); |
6894 | santize(Rd->body); | 8840 | santize(Rd->body); |
6895 | 8841 | ||
6896 | I("%s %s://%s%s -> %s%s", Rd->Method, Rd->Scheme, Rd->Host, Rd->RUri, webRoot, Rd->Path); | 8842 | D("%s %s://%s%s -> %s%s", Rd->Method, Rd->Scheme, Rd->Host, Rd->RUri, webRoot, Rd->Path); |
6897 | D("Started FCGI web request ROLE = %s, body is %s bytes, pid %d.", Role, Length, getpid()); | 8843 | D("Started FCGI web request ROLE = %s, body is %s bytes, pid %d.", Role, Length, getpid()); |
6898 | 8844 | ||
6899 | if (NULL == Rd->Path) | 8845 | if (NULL == Rd->Path) |
@@ -7084,6 +9030,7 @@ sendReply: | |||
7084 | 9030 | ||
7085 | fcgiDone: | 9031 | fcgiDone: |
7086 | FCGI_Finish(); | 9032 | FCGI_Finish(); |
9033 | snprintf(toybuf, sizeof(toybuf), " for %s %s://%s%s -> %s%s", Rd->Method, Rd->Scheme, Rd->Host, Rd->RUri, webRoot, Rd->Path); | ||
7087 | if (NULL != Rd->outQuery) free(Rd->outQuery); | 9034 | if (NULL != Rd->outQuery) free(Rd->outQuery); |
7088 | if (NULL != Rd->shs.name) free(Rd->shs.name); | 9035 | if (NULL != Rd->shs.name) free(Rd->shs.name); |
7089 | Rd->shs.name = NULL; | 9036 | Rd->shs.name = NULL; |
@@ -7109,122 +9056,18 @@ fcgiDone: | |||
7109 | perror_msg("Unable to get the time."); | 9056 | perror_msg("Unable to get the time."); |
7110 | double n = (now.tv_sec * 1000000000.0) + now.tv_nsec; | 9057 | double n = (now.tv_sec * 1000000000.0) + now.tv_nsec; |
7111 | double t = (Rd->then.tv_sec * 1000000000.0) + Rd->then.tv_nsec; | 9058 | double t = (Rd->then.tv_sec * 1000000000.0) + Rd->then.tv_nsec; |
7112 | I("Finished web request, took %lf seconds", (n - t) / 1000000000.0); | 9059 | T("Finished web request, took %lf seconds%s", (n - t) / 1000000000.0, toybuf); |
7113 | free(Rd); | 9060 | free(Rd); |
7114 | } | 9061 | } |
7115 | 9062 | ||
7116 | FCGI_fprintf(FCGI_stderr, "Stopped SledjChisl web server.\n"); | 9063 | FCGI_fprintf(FCGI_stderr, "Stopped SledjChisl web server.\n"); |
7117 | D("Stopped SledjChisl web server."); | 9064 | V("Stopped SledjChisl web server."); |
7118 | |||
7119 | goto finished; | ||
7120 | } | 9065 | } |
9066 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
9067 | // End of web wrangling section. | ||
9068 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
7121 | 9069 | ||
7122 | 9070 | ||
7123 | if (!isTmux) | ||
7124 | { // Let's see if the proper tmux server is even running. | ||
7125 | memset(toybuf, 0, sizeof(toybuf)); | ||
7126 | snprintf(toybuf, sizeof(toybuf), "%s %s/%s -q list-sessions 2>/dev/null | grep -q %s:", Tcmd, scRun, Tsocket, Tconsole); | ||
7127 | i = system(toybuf); | ||
7128 | if (WIFEXITED(i)) | ||
7129 | { | ||
7130 | if (0 != WEXITSTATUS(i)) // No such sesion, create it. | ||
7131 | { | ||
7132 | memset(toybuf, 0, sizeof(toybuf)); | ||
7133 | // 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. | ||
7134 | // After the session is created, we rely on the scRun directory to be group sticky, so that anyone in the opensim group can attach to the tmux socket. | ||
7135 | snprintf(toybuf, sizeof(toybuf), | ||
7136 | "sudo -Hu %s %s %s/%s new-session -d -s %s -n '%s' \\; split-window -bhp 50 -t '%s:' bash -c '/usr/bin/valgrind --leak-check=full ./sledjchisl; cd %s; bash'", | ||
7137 | scUser, Tcmd, scRun, Tsocket, Tconsole, Ttab, Tconsole, scRoot); | ||
7138 | i = system(toybuf); | ||
7139 | if (!WIFEXITED(i)) | ||
7140 | E("tmux new-session command failed! %s", toybuf); | ||
7141 | } | ||
7142 | // Join the session. | ||
7143 | memset(toybuf, 0, sizeof(toybuf)); | ||
7144 | snprintf(toybuf, sizeof(toybuf), "%s %s/%s select-window -t '%s' \\; attach-session -t '%s'", Tcmd, scRun, Tsocket, Tconsole, Tconsole); | ||
7145 | i = system(toybuf); | ||
7146 | if (!WIFEXITED(i)) | ||
7147 | E("tmux attach-session command failed! %s", toybuf); | ||
7148 | goto finished; | ||
7149 | } | ||
7150 | else | ||
7151 | E("tmux list-sessions command failed! %s", toybuf); | ||
7152 | } | ||
7153 | |||
7154 | |||
7155 | simList *sims = getSims(); | ||
7156 | if (1) | ||
7157 | { | ||
7158 | struct sysinfo info; | ||
7159 | float la; | ||
7160 | |||
7161 | sysinfo(&info); | ||
7162 | la = info.loads[0]/65536.0; | ||
7163 | |||
7164 | if (!checkSimIsRunning("ROBUST")) | ||
7165 | { | ||
7166 | char *d = xmprintf("%s.{right}", Ttab); | ||
7167 | char *c = xmprintf("cd %s/current/bin", scRoot); | ||
7168 | |||
7169 | I("ROBUST is starting up."); | ||
7170 | sendTmuxCmd(d, c); | ||
7171 | free(c); | ||
7172 | c = xmprintf("mono Robust.exe -inidirectory=%s/config/ROBUST", scRoot); | ||
7173 | sendTmuxCmd(d, c); | ||
7174 | free(c); | ||
7175 | waitTmuxText(d, "INITIALIZATION COMPLETE FOR ROBUST"); | ||
7176 | I("ROBUST is done starting up."); | ||
7177 | la = waitLoadAverage(la, loadAverageInc / 3.0, simTimeOut / 3); | ||
7178 | free(d); | ||
7179 | } | ||
7180 | |||
7181 | for (i = 0; i < sims->num; i++) | ||
7182 | { | ||
7183 | char *sim = sims->sims[i], *name = getSimName(sims->sims[i]); | ||
7184 | |||
7185 | if (!checkSimIsRunning(sim)) | ||
7186 | { | ||
7187 | char *nm = cleanSimName(name); | ||
7188 | |||
7189 | I("%s is starting up.", name); | ||
7190 | memset(toybuf, 0, sizeof(toybuf)); | ||
7191 | 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'", | ||
7192 | Tcmd, scRun, Tsocket, nm, Tconsole, i + 1, scRoot, scRoot, sim); | ||
7193 | int r = system(toybuf); | ||
7194 | if (!WIFEXITED(r)) | ||
7195 | E("tmux new-window command failed!"); | ||
7196 | else | ||
7197 | { | ||
7198 | memset(toybuf, 0, sizeof(toybuf)); | ||
7199 | snprintf(toybuf, sizeof(toybuf), "INITIALIZATION COMPLETE FOR %s", name); | ||
7200 | waitTmuxText(nm, toybuf); | ||
7201 | I("%s is done starting up.", name); | ||
7202 | la = waitLoadAverage(la, loadAverageInc, simTimeOut); | ||
7203 | } | ||
7204 | free(nm); | ||
7205 | } | ||
7206 | free(name); | ||
7207 | } | ||
7208 | |||
7209 | } | ||
7210 | else if (!strcmp(cmd, "create")) // "create name x,y size" | ||
7211 | { | ||
7212 | } | ||
7213 | else if (!strcmp(cmd, "start")) // "start sim01" "start Welcome" "start" start everything | ||
7214 | { | ||
7215 | // TODO - check if sim is down, but tmux window is still up, and close the tmux window first. | ||
7216 | } | ||
7217 | else if (!strcmp(cmd, "backup")) // "backup onefang rejected" "backup sim01" "backup Welcome" "backup" backup everything | ||
7218 | { // If it's not a sim code, and not a sim name, it's an account inventory. | ||
7219 | } | ||
7220 | else if (!strcmp(cmd, "gitAR")) // "gitAR i name" | ||
7221 | { | ||
7222 | } | ||
7223 | else if (!strcmp(cmd, "stop")) // "stop sim01" "stop Welcome" "stop" stop everything | ||
7224 | { | ||
7225 | } | ||
7226 | |||
7227 | freeSimList(sims); | ||
7228 | 9071 | ||
7229 | /* | 9072 | /* |
7230 | double sum; | 9073 | double sum; |
@@ -7289,6 +9132,8 @@ fcgiDone: | |||
7289 | */ | 9132 | */ |
7290 | 9133 | ||
7291 | finished: | 9134 | finished: |
9135 | freeSimList(ourSims); | ||
9136 | ourSims = NULL; | ||
7292 | 9137 | ||
7293 | // An example of calling a toy directly. | 9138 | // An example of calling a toy directly. |
7294 | // printf("\n\n"); | 9139 | // printf("\n\n"); |
@@ -7298,6 +9143,4 @@ finished: | |||
7298 | 9143 | ||
7299 | puts("This is the end my friend."); | 9144 | puts("This is the end my friend."); |
7300 | fflush(stdout); | 9145 | fflush(stdout); |
7301 | |||
7302 | cleanup(); | ||
7303 | } | 9146 | } |