aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/src/sledjchisl
diff options
context:
space:
mode:
authoronefang2021-08-26 06:21:19 +1000
committeronefang2021-08-26 06:21:19 +1000
commitcdfbb899f1112dab44d5490838765e9bd73bc60e (patch)
tree52cddd0b76e7ad8544a0ada533f91bb5fc402025 /src/sledjchisl
parentStill failing to reconnect for dbCount(), just set the fucking timeout to a y... (diff)
parentDon't strip (OWNER) out of script error report. (diff)
downloadopensim-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.c2971
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
6USE_SLEDJCHISL(NEWTOY(sledjchisl, "m(mode):", TOYFLAG_USR|TOYFLAG_BIN)) 6USE_SLEDJCHISL(NEWTOY(sledjchisl, "?vqma", TOYFLAG_USR|TOYFLAG_BIN))
7 7
8config SLEDJCHISL 8config 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
70GLOBALS(
71)
72
73/*
74 configs/sim01 -
75 backup-sim
76 start-sim
77 stop-sim
78
79Currently they are all links to ../../current/scripts/start-sim,
80they should be links to ../../current/bin/sledjchisl,
81which itself is a link to ../src/build/toybox/generated/unstripped/toybox or ../src/build/toybox/toybox
82They 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. 148typedef 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,
75GLOBALS( 153 RESTART = 3,
76 char *mode; 154 STATUS = 4,
77) 155 STOP = 9
78 156} modes;
79#define TT this.sledjchisl 157
80 158modes currentMode = START;
81#define FLAG_m 2 159
82 160char *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>
288int getrandom(void *b, size_t l, unsigned int f) 377ssize_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 = "";
427char *scCache = ""; 516char *scCache = "";
428char *scData = ""; 517char *scData = "";
429char *scLog = ""; 518char *scLog = "";
519char *scTemp = "";
430char *Tconsole = "SledjChisl"; 520char *Tconsole = "SledjChisl";
431char *Tsocket = "opensim-tmux.socket"; 521char *Tsocket = "opensim-tmux.socket";
432char *Ttab = "SC"; 522char *Ttab = "SC";
433char *Tcmd = "tmux -S"; 523char *Tcmd = "tmux -S";
524char *backupIARsim = "Sandbox";
525char *rSync = "";
526int rSyncPort = 0;
434char *webRoot = "/var/www/html"; 527char *webRoot = "/var/www/html";
435char *URL = "fcgi-bin/sledjchisl.fcgi"; 528char *URL = "fcgi-bin/sledjchisl.fcgi";
436char *ToS = "Be good."; 529char *ToS = "Be good.";
@@ -439,9 +532,11 @@ int seshRenew = 10 * 60;
439int idleTimeOut = 30 * 60; 532int idleTimeOut = 30 * 60;
440int seshTimeOut = 24 * 60 * 60; 533int seshTimeOut = 24 * 60 * 60;
441int newbieTimeOut = 30; 534int newbieTimeOut = 30;
442float loadAverageInc = 0.5; 535float loadAverageInc = 0.7;
443int simTimeOut = 45; 536int simTimeOut = 45;
444boolean DEBUG = TRUE; 537int bulkSims = 0;
538boolean DEBUG = FALSE;
539boolean VERBOSE = FALSE;
445qhashtbl_t *mimeTypes; 540qhashtbl_t *mimeTypes;
446qlist_t *dbRequests; 541qlist_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
614int 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}
636int 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
659int 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
682int 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
706int 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
510static void addStrL(qlist_t *list, char *s) 719static 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
735static 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."
742https://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.
745I 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*/
748static 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
526char *myHMAC(char *in, boolean b64) 757char *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
805typedef struct _qLua qLua;
806struct _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
575int sendTmuxKeys(char *dest, char *keys) 819qLua *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
824void 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
852qtreetbl_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
587int sendTmuxCmd(char *dest, char *cmd) 913qtreetbl_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
946void 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
964int 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
1027int 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
1052boolean 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
1068boolean 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
1084boolean 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
1130void 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
1152void sendTmuxCmd(char *dest, char *cmd)
1153{
1154 doTmuxCmd("send-keys -t %s:'%s' '%s' Enter", Tconsole, dest, cmd);
1155}
1156
1157void sendTmuxKeys(char *dest, char *keys)
1158{
1159 doTmuxCmd("send-keys -t %s:%s '%s'", Tconsole, dest, keys);
1160}
1161
599void waitTmuxText(char *dest, char *text) 1162void 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.
648struct dirtree *dirtree_handle_callback(struct dirtree *new, int (*callback)(struct dirtree *node)); 1211struct 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.
1215typedef struct _simData simData;
1216struct _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
650typedef struct _simList simList; 1225typedef struct _simList simList;
651struct _simList 1226struct _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};
1236simList *ourSims = NULL;
1237
1238
1239static 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
657static int filterSims(struct dirtree *node) 1252static 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 \ " `
676char *cleanSimName(char *name) 1272char *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
694simList *getSims() 1291static 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
1303static 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////////////////////////////////////////////////////////////////////////////////////////////////////
1325typedef void (*simFunction)(simData *simd, char *sim, char *type, int count, int window, int panes, int pane);
1326void 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
1395void 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
708void freeSimList(simList *sims) 1424void 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
718static 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.
1473simList *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
730char *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".
780int checkSimIsRunning(char *sim) 1726int 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
1794void 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
1809void 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.
1883void 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
1894void 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
1921void 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
1973typedef struct _ARList ARList;
1974struct _ARList
1975{
1976 int len, num;
1977 char **ARs, *this;
1978};
1979
1980static 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.
2001my_ulonglong dbCount(char *table, char *where);
2002void 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?
2022byTab 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 }
2290gitARend:
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
2348void 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
842static void PrintEnv(qgrow_t *reply, char *label, char **envp) 2370static 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
3950void 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
3970qlist_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
2423enum fragmentType 3990enum fragmentType
@@ -3216,28 +4783,6 @@ https://stackoverflow.com/questions/16891729/best-practices-salting-peppering-pa
3216qlisttbl_t *accountLevels = NULL; 4783qlisttbl_t *accountLevels = NULL;
3217 4784
3218 4785
3219static 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."
3226https://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.
3229I 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*/
3232static 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.
3242char *newSLOSsalt(reqData *Rd) 4787char *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
3266T("checkSLOSpassword(%s, %s, %s, ", password, salt, passwordHash, fail); 4811t("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
3305int 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));
3350d("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
3373char *checkLinky(reqData *Rd) 4852char *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. &nbsp; " 4861 ret = xmprintf("<p><font color='red'><b>You have an email waiting with a validation link in it, please check your email. &nbsp; "
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. &nbsp; " 4863 "It will be from %s@%s, and it might be in your spam folder, coz these sorts of emails sometimes end up there. &nbsp; "
3384 "You should add that email address to your contacts, or otherwise let it through your spam filter. &nbsp; " 4864 "You should add that email address to your contacts, or otherwise let it through your spam filter. &nbsp; "
4865 "It will be from %s@%s, and it might be in your spam folder, coz these sorts of emails sometimes end up there. &nbsp; "
4866 "You should add that email address to your contacts, or otherwise let it through your spam filter. &nbsp; "
4867 "It will be from %s@%s, and it might be in your spam folder, coz these sorts of emails sometimes end up there. &nbsp; "
4868 "You should add that email address to your contacts, or otherwise let it through your spam filter. &nbsp; "
4869 "It will be from %s@%s, and it might be in your spam folder, coz these sorts of emails sometimes end up there. &nbsp; "
4870 "You should add that email address to your contacts, or otherwise let it through your spam filter. &nbsp; "
4871 "If your email client wont let you click the validation link, just copy and paste it into your web browser. &nbsp; "
4872 "If your email client wont let you click the validation link, just copy and paste it into your web browser. &nbsp; "
4873 "If your email client wont let you click the validation link, just copy and paste it into your web browser. &nbsp; "
4874 "If your email client wont let you click the validation link, just copy and paste it into your web browser. &nbsp; "
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
3733boolean 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
3749boolean 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
3765boolean 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
3810static void accountWrite(reqData *Rd) 5233static 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
4070T(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)); 5498V(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 -
4202DAVEE (Delete, Add, View, Edit, Explore) 5616DAVEE (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 {
5231T("Found Lua record."); 6640V("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;
5260T("Found database record."); 6670V("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
7762void 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
6333static void cleanup(void) 7838static 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
7926int 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////////////////////////////////////////////////////////////////////////////////////////////////////
6402void sledjchisl_main(void) 7951void 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 -
6474To change or extend the list of standard libraries to load, copy 8034To change or extend the list of standard libraries to load, copy
6475src/lib_init.c to your project and modify it accordingly. Make sure the 8035src/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.
8041Normally 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
8045Other 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
8136Grab old database details from the config/config.ini file.
8137
8138Check 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
8142Make sure the database is running.
8143 sudo /etc/init.d/mysql restart
8144
8145Setup 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
8153Deal with the system user.
8154 sudo adduser --system --shell /bin/bash --group ${OS_USER}
8155 sudo addgroup ${USER} ${OS_USER}
8156
8157Copy the tmux config to user's homes.
8158
8159Copy this version of opensim-SC to /opt/opensim-SC, and make the current symlink to it.
8160
8161Copy correct config/config.ini to there.
8162 Put new database credentials in config/config.ini
8163
8164Build 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.
8530Make sure correct permissions are set everywhere.
8531 sudo chown -R ${OS_USER}:${OS_USER} ${OS_PATH}
8532
8533Create 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
7085fcgiDone: 9031fcgiDone:
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
7291finished: 9134finished:
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}