aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/src/sledjchisl/sledjchisl.c
diff options
context:
space:
mode:
authoronefang2021-07-06 14:12:03 +1000
committeronefang2021-07-06 14:12:03 +1000
commit9df93a76177822feb036e2875ad09f35627c53c3 (patch)
tree70ce139ea0330a64ce03f2baa3859d8cf27eb371 /src/sledjchisl/sledjchisl.c
parentAdd some libraries to sledjchisl. (diff)
downloadopensim-SC-9df93a76177822feb036e2875ad09f35627c53c3.zip
opensim-SC-9df93a76177822feb036e2875ad09f35627c53c3.tar.gz
opensim-SC-9df93a76177822feb036e2875ad09f35627c53c3.tar.bz2
opensim-SC-9df93a76177822feb036e2875ad09f35627c53c3.tar.xz
Lots of SledjChisl changes.
New command argument syntax. Method of starting sims up in bulk, depending on number of CPUs. Load all the sim config data at sledjchisl startup. Convert to new .shini polyglot format for sim .ini files. Lets them be scripts as well. Also moves them out of config/sim??/ directories. Short sim names for where short legal names are best. Figure out port numbers on the fly, putting the actual config file used in tmp. Alas OpenSim screws the pooch and we need sim??/*.ini for that. Extra text in validation emails, trying to get around dumb spam filters. 2021 age check update. Add approver to Lua user records when approved. Refactor the command checking and doing code. Allow admins to group sims for startup timing. Move simple backup script into sledjchisl. Startup web stuff at end.
Diffstat (limited to '')
-rw-r--r--src/sledjchisl/sledjchisl.c898
1 files changed, 693 insertions, 205 deletions
diff --git a/src/sledjchisl/sledjchisl.c b/src/sledjchisl/sledjchisl.c
index 42ed205..576f58f 100644
--- a/src/sledjchisl/sledjchisl.c
+++ b/src/sledjchisl/sledjchisl.c
@@ -2,18 +2,59 @@
2 * 2 *
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 *
6 * Not using the usual taybox args system, coz it's not working as advertised.
5 7
6USE_SLEDJCHISL(NEWTOY(sledjchisl, "m(mode):", TOYFLAG_USR|TOYFLAG_BIN)) 8USE_SLEDJCHISL(NEWTOY(sledjchisl, "", TOYFLAG_USR|TOYFLAG_BIN))
7 9
8config SLEDJCHISL 10config SLEDJCHISL
9 bool "sledjchisl" 11 bool "sledjchisl"
10 default y 12 default y
11 help 13 help
12 usage: sledjchisl [-m|--mode mode] 14 usage: sledjchisl [mode [arguments]]
13 15
14 opensim-SC management system. 16 opensim-SC management system.
17
18 Mode selects what sledjchisl will do -
19 create "Sim Name" x,y size
20 will create a sim.
21 start
22 start sim01
23 start "Welcome sim"
24 will start a sim or everything.
25 backup
26 backup "Joan Smith"
27 backup sim01
28 backup "Welcome sim"
29 will backup sim or everything.
30 gitar i "Joan Smith"
31 gitar o sim01
32 gitar o "Welcome sim"
33 restart
34 restart sim01
35 restart "Welcome sim"
36 will stop then start a sim, or everything.
37 status
38 status sim01
39 status "Welcome sim"
40 will show status of a sim, or everything.
41 stop
42 stop sim01
43 stop "Welcome sim"
44 will stop a sim, or everything.
15*/ 45*/
16 46
47/*
48 configs/sim01 -
49 backup-sim
50 start-sim
51 stop-sim
52
53Currently they are all links to ../../current/scripts/start-sim,
54they should be links to ../../current/bin/sledjchisl,
55which itself is a link to ../src/build/toybox/generated/unstripped/toybox or ../src/build/toybox/toybox
56
57*/
17 58
18// TODO - figure out how to automate testing of this. 59// TODO - figure out how to automate testing of this.
19// Being all interactive and involving external web servers / viewers makes it hard. 60// Being all interactive and involving external web servers / viewers makes it hard.
@@ -54,7 +95,7 @@ extern char **environ;
54// https://mariadb.com/kb/en/about-mariadb-connector-c/ Official docs. 95// 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. 96// http://dev.mysql.com/doc/refman/5.5/en/c-api-function-overview.html MySQL docs.
56// http://zetcode.com/db/mysqlc/ MySQL tutorial. 97// http://zetcode.com/db/mysqlc/ MySQL tutorial.
57#include <my_global.h> 98//#include <my_global.h>
58#include <mysql.h> 99#include <mysql.h>
59 100
60#include <qlibc.h> 101#include <qlibc.h>
@@ -71,16 +112,18 @@ extern char **environ;
71// I deal with that by using a sed invokation when building toybox. 112// I deal with that by using a sed invokation when building toybox.
72#include "toys.h" 113#include "toys.h"
73 114
115typedef enum
116{
117 CREATE = 0,
118 START = 1,
119 BACKUP = 2,
120 GITAR = 3,
121 RESTART = 4,
122 STATUS = 5,
123 STOP = 9
124} modes;
74 125
75GLOBALS( 126modes currentMode = START;
76 char *mode;
77)
78
79#define TT this.sledjchisl
80
81#define FLAG_m 2
82
83
84 127
85// Duplicate some small amount of code from qLibc, coz /, + and, = are not good choices, and the standard says we can pick those. 128// Duplicate some small amount of code from qLibc, coz /, + and, = are not good choices, and the standard says we can pick those.
86/** 129/**
@@ -282,10 +325,10 @@ typedef enum
282// Silly "man getrandom" is bullshitting. 325// Silly "man getrandom" is bullshitting.
283// Note - this is Linux specific, it's calling a Linux kernel function. 326// 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 - 327// Remove this when we have a real getrandom(), and replace it with -
285// #include <sys/random.h> 328//#include <sys/random.h>
286#include <sys/syscall.h> 329#include <sys/syscall.h>
287#include <linux/random.h> 330#include <linux/random.h>
288int getrandom(void *b, size_t l, unsigned int f) 331ssize_t getrandom(void *b, size_t l, unsigned int f)
289{ 332{
290 return (int) syscall(SYS_getrandom, b, l, f); 333 return (int) syscall(SYS_getrandom, b, l, f);
291} 334}
@@ -427,6 +470,7 @@ char *scBackup = "";
427char *scCache = ""; 470char *scCache = "";
428char *scData = ""; 471char *scData = "";
429char *scLog = ""; 472char *scLog = "";
473char *scTemp = "";
430char *Tconsole = "SledjChisl"; 474char *Tconsole = "SledjChisl";
431char *Tsocket = "opensim-tmux.socket"; 475char *Tsocket = "opensim-tmux.socket";
432char *Ttab = "SC"; 476char *Ttab = "SC";
@@ -439,8 +483,9 @@ int seshRenew = 10 * 60;
439int idleTimeOut = 30 * 60; 483int idleTimeOut = 30 * 60;
440int seshTimeOut = 24 * 60 * 60; 484int seshTimeOut = 24 * 60 * 60;
441int newbieTimeOut = 30; 485int newbieTimeOut = 30;
442float loadAverageInc = 0.5; 486float loadAverageInc = 0.7;
443int simTimeOut = 45; 487int simTimeOut = 45;
488int bulkSims = 0;
444boolean DEBUG = TRUE; 489boolean DEBUG = TRUE;
445qhashtbl_t *mimeTypes; 490qhashtbl_t *mimeTypes;
446qlist_t *dbRequests; 491qlist_t *dbRequests;
@@ -463,7 +508,7 @@ char *logTypes[] =
463 "36", "TIMEOUT", // cyan 508 "36", "TIMEOUT", // cyan
464 "97;40", "INFO", // white 509 "97;40", "INFO", // white
465 "90", "DEBUG", // grey 510 "90", "DEBUG", // grey
466// VERBOSE? UNKNOWN? FATAL? SILENT? All from Android aparently. 511// VERBOSE? UNKNOWN? FATAL? SILENT? All from Android apparently.
467 "35", "debug", // magenta 512 "35", "debug", // magenta
468 "34", "timeout", // blue 513 "34", "timeout", // blue
469}; 514};
@@ -647,12 +692,38 @@ float waitLoadAverage(float la, float extra, int timeout)
647// Rob forget to do this, but at least he didn't declare it static. 692// 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)); 693struct dirtree *dirtree_handle_callback(struct dirtree *new, int (*callback)(struct dirtree *node));
649 694
695
696// A sim structure for holding all the stuff from the sim.ini file.
697typedef struct _simData simData;
698struct _simData
699{
700 // portH is the HTTP port for the sim, portI is the UDP port for the sim.
701 int num, locX, locY, sizeX, sizeY, sizeZ, portH, portI, maxPrims;
702 char *name, *tab, *UUID, *regionType, *estate, *owner;
703// char *nmbr;
704};
705
650typedef struct _simList simList; 706typedef struct _simList simList;
651struct _simList 707struct _simList
652{ 708{
653 int len, num; 709 int len, num;
654 char **sims; 710 char **sims;
711 qtreetbl_t *tbl, *byTab;
655}; 712};
713simList *ourSims = NULL;
714
715static int getIntFromIni(qlisttbl_t *ini, char *name)
716{
717 int ret;
718 char *t = "0";
719
720 t = ini->getstr(ini, name, false);
721 if (NULL == t)
722 t = "0";
723 else if ('"' == t[0])
724 t = qstrunchar(t, '"', '"');
725 return strtol(t, NULL, 10);
726}
656 727
657static int filterSims(struct dirtree *node) 728static int filterSims(struct dirtree *node)
658{ 729{
@@ -672,6 +743,7 @@ static int filterSims(struct dirtree *node)
672 return 0; 743 return 0;
673} 744}
674 745
746/*
675// We particularly don't want \ " ` 747// We particularly don't want \ " `
676char *cleanSimName(char *name) 748char *cleanSimName(char *name)
677{ 749{
@@ -690,19 +762,163 @@ char *cleanSimName(char *name)
690 762
691 return ret; 763 return ret;
692} 764}
765*/
766
767static int filterInis(struct dirtree *node)
768{
769 if (!node->parent) return DIRTREE_RECURSE | DIRTREE_SHUTUP;
770 int l = strlen(node->name);
771 if (strncmp(&(node->name[l - 4]), ".ini", 4) == 0)
772 {
773 strncpy((char *) node->parent->extra, node->name, l - 4);
774 return DIRTREE_ABORT;
775 }
776 return 0;
777}
693 778
694simList *getSims() 779simList *getSims()
695{ 780{
696 simList *sims = xmalloc(sizeof(simList)); 781 if (NULL != ourSims) return ourSims;
697 memset(sims, 0, sizeof(simList)); 782
698 char *path = xmprintf("%s/config", scRoot); 783 char *path = xmprintf("%s/config", scRoot), *newPath;
699 struct dirtree *new = dirtree_add_node(0, path, 0); 784 struct dirtree *new = dirtree_add_node(0, path, 0);
700 new->extra = (long) sims; 785 int i, j;
786
787 ourSims = xmalloc(sizeof(simList));
788 memset(ourSims, 0, sizeof(simList));
789
790 ourSims->tbl = qtreetbl(0);
791 ourSims->byTab = qtreetbl(0);
792 new->extra = (long) ourSims;
701 dirtree_handle_callback(new, filterSims); 793 dirtree_handle_callback(new, filterSims);
702 794
703 qsort(sims->sims, sims->num, sizeof(char *), qstrcmp); 795 qsort(ourSims->sims, ourSims->num, sizeof(char *), qstrcmp);
704 free(path); 796 free(path);
705 return sims; 797
798
799 char *file = xmprintf("%s/sims.lua", scEtc);
800 char *tnm = "sims = -- Note these are .shini / tmux tab short names.\n{\n {['type'] = 'unsorted';\n";
801 struct stat st;
802 int s = stat(file, &st);
803 int fd = -1;
804 size_t l = strlen(tnm);
805
806 if (s)
807 {
808 I("Creating sims %s.", file);
809 fd = notstdio(xcreate_stdio(file, O_CREAT | O_WRONLY | O_TRUNC | O_CLOEXEC, S_IRUSR | S_IWUSR));
810 }
811
812 if (-1 != fd)
813 {
814 if (l != writeall(fd, tnm, l))
815 perror_msg("Writing %s", file);
816 }
817
818
819 for (i = 0; i < ourSims->num; i++)
820 {
821 char *sim = ourSims->sims[i], *name = xmprintf("%s/config/%s", scRoot, sim);
822 qlisttbl_t *ini;
823 simData *simd = xmalloc(sizeof(simData));
824
825 struct dirtree *new = dirtree_add_node(0, name, 0);
826
827 free(name);
828 name = xzalloc(1024);
829 new->extra = (long) name;
830 dirtree_handle_callback(new, filterInis);
831 if ('\0' != name[0])
832 {
833 path = xmprintf("%s/config/%s/%s.ini", scRoot, sim, name);
834 newPath = xmprintf("%s/%s.shini", scEtc, name);
835 I("Reading .ini file %s", path);
836 ini = qconfig_parse_file(NULL, path, '=');
837
838ini->putstr(ini, "INI FILE", name);
839/*
840 [Region]
841 Location = "1,1"
842 InternalPort = "9008"
843 MaxPrims = 45000
844
845 [Network]
846 http_listener_port = 9007
847*/
848 simd->num = getIntFromIni(ini, "Const.mysim");
849 simd->name = qstrunchar(ini->getstr(ini, "Region.RegionName", false), '"', '"');
850 simd->UUID = qstrunchar(ini->getstr(ini, "Region.RegionUUID", false), '"', '"');
851 simd->regionType = qstrunchar(ini->getstr(ini, "Region.RegionType", false), '"', '"');
852 simd->sizeX = getIntFromIni(ini, "Region.SizeX");
853 simd->sizeY = getIntFromIni(ini, "Region.SizeY");
854 simd->sizeZ = getIntFromIni(ini, "Region.SizeZ");
855 simd->tab = name;
856// simd->nmbr = sim;
857 ini->put(ini, "SIM DATA", simd, sizeof(simData));
858ourSims->tbl->put(ourSims->tbl, sim, ini, sizeof(qlisttbl_t));
859 ourSims->byTab->put(ourSims->byTab, name, ini, sizeof(qlisttbl_t));
860
861 if ((!qfile_exist(newPath)))
862 {
863 char *cmd = xmprintf("sed -E"
864 " -e 's#\\[Const]#\\[Const] ; fakeVariableCozOpenSim='' ; pushd ../current/bin; ./sledjchisl $1 `basename $0`; popd ; exit 0#'"
865 " -e 's/mysim=\"[[:digit:]]*\"/mysim=\"%s\"/'"
866 " -e 's/sim\\$\\{Const\\|mysim\\}/\\$\\{Const\\|mysim\\}/g'"
867 " %s >%s", simd->tab, path, newPath);
868
869 I("Writing .shini file %s", newPath);
870 D(cmd);
871 j = system(cmd);
872 if (!WIFEXITED(j))
873 E("sed command failed!");
874 else
875 {
876 free(cmd);
877 cmd = xmprintf("chmod ugo+x %s", newPath);
878 j = system(cmd);
879 if (!WIFEXITED(j))
880 E("chmod command failed!");
881
882
883 char *link = xmprintf("%s/%s.shini", scBin, simd->tab);
884 I("Symlinking %s to %s", newPath, link);
885 if (0 != symlink(newPath, link))
886 perror_msg("Symlinking %s to %s", newPath, link);
887 free(link);
888
889
890 }
891 free(cmd);
892 }
893 }
894 free(newPath);
895 free(path);
896
897
898 if (-1 != fd)
899 {
900 tnm = xmprintf(" '%s', \n", simd->tab);
901 l = strlen(tnm);
902 if (l != writeall(fd, tnm, l))
903 perror_msg("Writing %s", file);
904 free(tnm);
905 }
906
907 }
908
909 if (-1 != fd)
910 {
911 tnm = " },\n}\nreturn sims\n";
912 l = strlen(tnm);
913 if (l != writeall(fd, tnm, l))
914 perror_msg("Writing %s", file);
915
916 xclose(fd);
917 }
918 free(file);
919
920
921 return ourSims;
706} 922}
707 923
708void freeSimList(simList *sims) 924void freeSimList(simList *sims)
@@ -712,69 +928,44 @@ void freeSimList(simList *sims)
712 for (i = 0; i < sims->num; i++) 928 for (i = 0; i < sims->num; i++)
713 free(sims->sims[i]); 929 free(sims->sims[i]);
714 free(sims->sims); 930 free(sims->sims);
715 free(sims);
716}
717 931
718static int filterInis(struct dirtree *node) 932 qtreetbl_obj_t obj;
719{ 933 memset((void*) &obj, 0, sizeof(obj)); // start from the minimum.
720 if (!node->parent) return DIRTREE_RECURSE | DIRTREE_SHUTUP; 934 sims->tbl->lock(sims->tbl);
721 int l = strlen(node->name); 935 while (sims->tbl->getnext(sims->tbl, &obj, false) == true)
722 if (strncmp(&(node->name[l - 4]), ".ini", 4) == 0) 936 {
937 char *name = qmemdup(obj.name, obj.namesize); // keep the name
938 size_t namesize = obj.namesize; // for removal argument
939 qlisttbl_t *ini = (qlisttbl_t *) obj.data;
940 if (NULL != ini)
723 { 941 {
724 strcpy((char *) node->parent->extra, node->name); 942 simData *simd = ini->get(ini, "SIM DATA", NULL, false);
725 return DIRTREE_ABORT;
726 }
727 return 0;
728}
729 943
730char *getSimName(char *sim) 944 if (NULL != simd)
731{
732 char *ret = NULL;
733 char *c = xmprintf("%s/config/%s", scRoot, sim);
734 struct dirtree *new = dirtree_add_node(0, c, 0);
735
736 free(c);
737 c = xzalloc(1024);
738 new->extra = (long) c;
739 dirtree_handle_callback(new, filterInis);
740 if ('\0' != c[0])
741 {
742 char *temp = NULL;
743 regex_t pat;
744 regmatch_t m[2];
745 long len;
746 int fd;
747
748 temp = xmprintf("%s/config/%s/%s", scRoot, sim, c);
749 fd = xopenro(temp);
750 xregcomp(&pat, "RegionName = \"(.+)\"", REG_EXTENDED);
751 do
752 {
753 // TODO - get_line() is slow, and wont help much with DOS and Mac line endings.
754 // gio_gets() isn't any faster really, but deals with DOS line endings at least.
755 free(temp);
756 temp = get_line(fd);
757 if (temp)
758 { 945 {
759 if (!regexec(&pat, temp, 2, m, 0)) 946 free(simd->name);
760 { 947 free(simd->UUID);
761 // Return first parenthesized subexpression as string. 948 free(simd->regionType);
762 if (pat.re_nsub > 0) 949// free(simd->estate);
763 { 950// free(simd->owner);
764 ret = xmprintf("%.*s", (int) (m[1].rm_eo - m[1].rm_so), temp + m[1].rm_so); 951 free(simd);
765 free(temp);
766 break;
767 }
768 }
769 } 952 }
770 } while (temp); 953// TODO - this leaks memory, but it's a bug in qLibc. Send the bug fix upstream.
771 regfree(&pat); 954// It either leaks, or frees twice. Pffft
772 xclose(fd); 955// ini->clear(ini);
956// ini->free(ini);
957 ;
958 }
959
960 sims->tbl->remove_by_obj(sims->tbl, obj.name, obj.namesize); // remove
961 obj = sims->tbl->find_nearest(sims->tbl, name, namesize, false); // rewind one step back
962 free(name); // clean up
773 } 963 }
774 free(c); 964 sims->tbl->unlock(sims->tbl);
775 return ret; 965 sims->tbl->free(sims->tbl);
776}
777 966
967 free(sims);
968}
778 969
779// Expects either "simXX" or "ROBUST". 970// Expects either "simXX" or "ROBUST".
780int checkSimIsRunning(char *sim) 971int checkSimIsRunning(char *sim)
@@ -3421,6 +3612,7 @@ char *checkLinky(reqData *Rd)
3421 ret = xmprintf("<p><font color='red'><b>You have an email waiting with a validation link in it, please check your email. &nbsp; " 3612 ret = xmprintf("<p><font color='red'><b>You have an email waiting with a validation link in it, please check your email. &nbsp; "
3422 "It will be from %s@%s, and it might be in your spam folder, coz these sorts of emails sometimes end up there. &nbsp; " 3613 "It will be from %s@%s, and it might be in your spam folder, coz these sorts of emails sometimes end up there. &nbsp; "
3423 "You should add that email address to your contacts, or otherwise let it through your spam filter. &nbsp; " 3614 "You should add that email address to your contacts, or otherwise let it through your spam filter. &nbsp; "
3615 "If your email client wont let you click the validation link, just copy and paste it into your web browser. &nbsp; "
3424// "<a href='https://%s%s?hashish=%s'>%s</a>" 3616// "<a href='https://%s%s?hashish=%s'>%s</a>"
3425 "</b></font></p>\n", 3617 "</b></font></p>\n",
3426 "grid_no_reply", Rd->Host, 3618 "grid_no_reply", Rd->Host,
@@ -3646,15 +3838,22 @@ t("Write shs %s", tnm4);
3646 "\n" 3838 "\n"
3647 "Please go to this web link to validate your new account -\n" 3839 "Please go to this web link to validate your new account -\n"
3648 "https://%s%s?hashish=%s\n" 3840 "https://%s%s?hashish=%s\n"
3841 "If your email client wont let you click the validation\n"
3842 "link, just copy and paste it into your web browser.\n"
3843 "\n"
3649 "\n" 3844 "\n"
3650 "Do not reply to this email.\n" 3845 "Do not reply to this email.\n"
3651 "\n", 3846 "\n"
3847 "\n"
3848 "A copy of the grids Terms of Service that you agreed to\n"
3849 "when you created your account is included below.\n"
3850 "%s",
3652 Rd->Host, Rd->Host, Rd->Host, 3851 Rd->Host, Rd->Host, Rd->Host,
3653 getStrH(Rd->stuff, "email"), 3852 getStrH(Rd->stuff, "email"),
3654 Rd->Host, Rd->Host, 3853 Rd->Host, Rd->Host,
3655 first, last, 3854 first, last,
3656 first, last, Rd->Host, Rd->RUri, 3855 first, last, Rd->Host, Rd->RUri,
3657 Rd->Host, Rd->RUri, t1 3856 Rd->Host, Rd->RUri, t1, ToS
3658 ); 3857 );
3659 l = strlen(content); 3858 l = strlen(content);
3660 file = xmprintf("%s/sessions/%s.email", scCache, shs->leaf); 3859 file = xmprintf("%s/sessions/%s.email", scCache, shs->leaf);
@@ -3894,6 +4093,7 @@ static void accountWrite(reqData *Rd)
3894 if (!writeLuaString (Rd, fd, file, "aboutMe", NULL)) goto notWritten; 4093 if (!writeLuaString (Rd, fd, file, "aboutMe", NULL)) goto notWritten;
3895 if (!writeLuaString (Rd, fd, file, "vouched", "off")) goto notWritten; 4094 if (!writeLuaString (Rd, fd, file, "vouched", "off")) goto notWritten;
3896 if (!writeLuaString (Rd, fd, file, "voucher", NULL)) goto notWritten; 4095 if (!writeLuaString (Rd, fd, file, "voucher", NULL)) goto notWritten;
4096 if (!writeLuaString (Rd, fd, file, "approver", NULL)) goto notWritten;
3897 if (!writeLuaString (Rd, fd, file, "link", link)) goto notWritten; 4097 if (!writeLuaString (Rd, fd, file, "link", link)) goto notWritten;
3898 l = strlen(end); 4098 l = strlen(end);
3899 if (l != writeall(fd, end, l)) 4099 if (l != writeall(fd, end, l))
@@ -3939,7 +4139,7 @@ notWritten:
3939 "UserLevel", 4139 "UserLevel",
3940 "UserFlags", 4140 "UserFlags",
3941 "UserTitle", 4141 "UserTitle",
3942// "ServiceURLs", // No worky "text", filled with crap. 4142// "ServiceURLs", // No worky "text", filled with crap. Leaving it blank works fine.
3943 "active", 4143 "active",
3944 NULL 4144 NULL
3945 }; 4145 };
@@ -4090,16 +4290,26 @@ notWritten:
4090 } 4290 }
4091 4291
4092 // load iar -m first last / password /opt/opensim_SC/backups/DefaultMember.IAR 4292 // load iar -m first last / password /opt/opensim_SC/backups/DefaultMember.IAR
4093 simList *sims = getSims();
4094 struct sysinfo info; 4293 struct sysinfo info;
4095 float la; 4294 float la;
4096 4295
4097 sysinfo(&info); 4296 sysinfo(&info);
4098 la = info.loads[0]/65536.0; 4297 la = info.loads[0]/65536.0;
4099 4298
4100 for (i = 0; i < sims->num; i++) 4299 for (i = 0; i < ourSims->num; i++)
4101 { 4300 {
4102 char *sim = sims->sims[i], *name = getSimName(sims->sims[i]); 4301 char *sim = ourSims->sims[i], *name;// = getSimName(ourSims->sims[i], &nm, &num);
4302
4303 qlisttbl_t *ini = ourSims->tbl->get(ourSims->tbl, sim, NULL, false);
4304 if (NULL == ini)
4305 {
4306 E("Sim %s not found in ini list!", sim);
4307// name = getSimName(ourSims->sims[i], &nm, &num);
4308 }
4309 else
4310 name = qstrunchar(ini->getstr(ini, "Region.RegionName", false), '"', '"');
4311
4312
4103 4313
4104 if (checkSimIsRunning(sim)) 4314 if (checkSimIsRunning(sim))
4105 { 4315 {
@@ -4124,7 +4334,6 @@ T(c);
4124 } 4334 }
4125 free(name); 4335 free(name);
4126 } 4336 }
4127 freeSimList(sims);
4128 } 4337 }
4129 free(first); 4338 free(first);
4130 } 4339 }
@@ -4796,8 +5005,8 @@ static int DoBValidate(reqData *Rd, inputForm *iF, inputValue *iV)
4796 else 5005 else
4797 { 5006 {
4798 i = atoi(t0); 5007 i = atoi(t0);
4799// TODO - get this to use current year instead of 2020. 5008// TODO - get this to use current year instead of 2021.
4800 if ((1900 > i) || (i > 2020)) 5009 if ((1900 > i) || (i > 2021))
4801 { 5010 {
4802 bitch(Rd, "Please supply a year of birth.", "Out of range."); 5011 bitch(Rd, "Please supply a year of birth.", "Out of range.");
4803 ret++; 5012 ret++;
@@ -4807,7 +5016,7 @@ static int DoBValidate(reqData *Rd, inputForm *iF, inputValue *iV)
4807 bitch(Rd, "Please supply a proper year of birth.", "Out of range, too old."); 5016 bitch(Rd, "Please supply a proper year of birth.", "Out of range, too old.");
4808 ret++; 5017 ret++;
4809 } 5018 }
4810 else if (i >2004) 5019 else if (i >2005)
4811 { 5020 {
4812 bitch(Rd, "This grid is Adult rated, you are too young.", "Out of range, too young."); 5021 bitch(Rd, "This grid is Adult rated, you are too young.", "Out of range, too young.");
4813 ret++; 5022 ret++;
@@ -5074,6 +5283,7 @@ static void accountViewWeb(reqData *Rd, inputForm *oF, inputValue *oV)
5074 *level = getStrH(Rd->database, "UserAccounts.UserLevel"), 5283 *level = getStrH(Rd->database, "UserAccounts.UserLevel"),
5075 *email = getStrH(Rd->database, "UserAccounts.Email"), 5284 *email = getStrH(Rd->database, "UserAccounts.Email"),
5076 *voucher = getStrH(Rd->database, "Lua.voucher"), 5285 *voucher = getStrH(Rd->database, "Lua.voucher"),
5286 *approver = getStrH(Rd->database, "Lua.approver"),
5077 *about = getStrH(Rd->database, "Lua.aboutMe"); 5287 *about = getStrH(Rd->database, "Lua.aboutMe");
5078 time_t crtd = atol(getStrH(Rd->database, "UserAccounts.Created")); 5288 time_t crtd = atol(getStrH(Rd->database, "UserAccounts.Created"));
5079 5289
@@ -5086,6 +5296,7 @@ static void accountViewWeb(reqData *Rd, inputForm *oF, inputValue *oV)
5086 Rd->reply->addstrf(Rd->reply, "<p><font size='5'><span style='font-size: x-large'><b>Email :</b></span></font> %s</p>", email); 5296 Rd->reply->addstrf(Rd->reply, "<p><font size='5'><span style='font-size: x-large'><b>Email :</b></span></font> %s</p>", email);
5087 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")); 5297 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"));
5088 Rd->reply->addstrf(Rd->reply, "<p><font size='5'><span style='font-size: x-large'><b>Voucher :</b></span></font> %s</p>", voucher); 5298 Rd->reply->addstrf(Rd->reply, "<p><font size='5'><span style='font-size: x-large'><b>Voucher :</b></span></font> %s</p>", voucher);
5299 Rd->reply->addstrf(Rd->reply, "<p><font size='5'><span style='font-size: x-large'><b>Approver :</b></span></font> %s</p>", approver);
5089 HTMLtextArea(Rd->reply, "aboutMe", "About", 7, 50, 4, 16384, "", "off", "true", "soft", about, FALSE, TRUE); 5300 HTMLtextArea(Rd->reply, "aboutMe", "About", 7, 50, 4, 16384, "", "off", "true", "soft", about, FALSE, TRUE);
5090 accountWebSubs(Rd, oF); 5301 accountWebSubs(Rd, oF);
5091 accountWebFooter(Rd, oF); 5302 accountWebFooter(Rd, oF);
@@ -5097,6 +5308,7 @@ static void accountEditWeb(reqData *Rd, inputForm *oF, inputValue *oV)
5097 *level = getStrH(Rd->database, "UserAccounts.UserLevel"), 5308 *level = getStrH(Rd->database, "UserAccounts.UserLevel"),
5098 *email = getStrH(Rd->database, "UserAccounts.Email"), 5309 *email = getStrH(Rd->database, "UserAccounts.Email"),
5099 *voucher = getStrH(Rd->database, "Lua.voucher"), 5310 *voucher = getStrH(Rd->database, "Lua.voucher"),
5311 *approver = getStrH(Rd->database, "Lua.approver"),
5100 *about = getStrH(Rd->database, "Lua.aboutMe"), 5312 *about = getStrH(Rd->database, "Lua.aboutMe"),
5101 *lvl = getLevel(atoi(level)); 5313 *lvl = getLevel(atoi(level));
5102 short lv = atoi(level); 5314 short lv = atoi(level);
@@ -5117,6 +5329,7 @@ static void accountEditWeb(reqData *Rd, inputForm *oF, inputValue *oV)
5117 { 5329 {
5118 qlisttbl_obj_t obj; 5330 qlisttbl_obj_t obj;
5119 5331
5332 HTMLhidden(Rd->reply, "approver", approver);
5120 HTMLselect(Rd->reply, "level", "level"); 5333 HTMLselect(Rd->reply, "level", "level");
5121 memset((void*)&obj, 0, sizeof(obj)); // must be cleared before call 5334 memset((void*)&obj, 0, sizeof(obj)); // must be cleared before call
5122 accountLevels->lock(accountLevels); 5335 accountLevels->lock(accountLevels);
@@ -5288,6 +5501,7 @@ T("Found Lua record.");
5288 Rd->database->putstr(Rd->database, "Lua.aboutMe", getStrH(tnm, "aboutMe")); 5501 Rd->database->putstr(Rd->database, "Lua.aboutMe", getStrH(tnm, "aboutMe"));
5289 Rd->database->putstr(Rd->database, "Lua.vouched", getStrH(tnm, "vouched")); 5502 Rd->database->putstr(Rd->database, "Lua.vouched", getStrH(tnm, "vouched"));
5290 Rd->database->putstr(Rd->database, "Lua.voucher", getStrH(tnm, "voucher")); 5503 Rd->database->putstr(Rd->database, "Lua.voucher", getStrH(tnm, "voucher"));
5504 Rd->database->putstr(Rd->database, "Lua.approver", getStrH(tnm, "approver"));
5291 } 5505 }
5292// else if (rows) 5506// else if (rows)
5293 if (rows) 5507 if (rows)
@@ -5369,9 +5583,9 @@ static int accountDelSub(reqData *Rd, inputForm *iF, inputValue *iV)
5369 } 5583 }
5370 else 5584 else
5371 { 5585 {
5372// check if logged in user is allowed to delete this account 5586// TODO - check if logged in user is allowed to delete this account
5373// delete user record 5587// TODO - delete user record
5374// log the user out if they are logged in 5588// TODO - log the user out if they are logged in
5375 } 5589 }
5376 return ret; 5590 return ret;
5377} 5591}
@@ -5464,7 +5678,8 @@ static int accountSaveSub(reqData *Rd, inputForm *iF, inputValue *iV)
5464{ 5678{
5465 int ret = 0; 5679 int ret = 0;
5466 // Using body[user] here, coz we got to this page via a URL query. 5680 // Using body[user] here, coz we got to this page via a URL query.
5467 char *uuid = Rd->shs.UUID, *first = getStrH(Rd->body, "user"), *last = NULL; 5681 char *uuid = Rd->shs.UUID, *first = getStrH(Rd->body, "user"), *last = NULL,
5682 *level = getStrH(Rd->database, "UserAccounts.UserLevel");
5468 int c = accountRead(Rd, NULL, first, last); 5683 int c = accountRead(Rd, NULL, first, last);
5469 5684
5470 if (1 != c) 5685 if (1 != c)
@@ -5474,6 +5689,24 @@ static int accountSaveSub(reqData *Rd, inputForm *iF, inputValue *iV)
5474 } 5689 }
5475 else if ((0 == ret) && (strcmp("POST", Rd->Method) == 0)) 5690 else if ((0 == ret) && (strcmp("POST", Rd->Method) == 0))
5476 { 5691 {
5692 char *lvl = getStrH(Rd->body, "level");
5693 qlisttbl_obj_t obj;
5694
5695 if (strcmp(level, lvl) != 0)
5696 {
5697 if (200 <= Rd->shs.level)
5698 {
5699 I("%s %s approved by %s.", getStrH(Rd->database, "UserAccounts.FirstName"), getStrH(Rd->database, "UserAccounts.LastName"), Rd->shs.name);
5700 Rd->stuff->putstr(Rd->stuff, "approver", Rd->shs.name);
5701 }
5702 else
5703 {
5704 bitch(Rd, "Cannot change level.", "User level not high enough.");
5705 lvl = level;
5706 Rd->stuff->putstr(Rd->stuff, "approver", getStrH(Rd->database, "Lua.approver"));
5707 }
5708 }
5709
5477 Rd->stuff->putstr(Rd->stuff, "email", getStrH(Rd->database, "UserAccounts.Email")); 5710 Rd->stuff->putstr(Rd->stuff, "email", getStrH(Rd->database, "UserAccounts.Email"));
5478 Rd->stuff->putstr(Rd->stuff, "created", getStrH(Rd->database, "UserAccounts.Created")); 5711 Rd->stuff->putstr(Rd->stuff, "created", getStrH(Rd->database, "UserAccounts.Created"));
5479 Rd->stuff->putstr(Rd->stuff, "flags", getStrH(Rd->database, "UserAccounts.UserFlags")); 5712 Rd->stuff->putstr(Rd->stuff, "flags", getStrH(Rd->database, "UserAccounts.UserFlags"));
@@ -5488,15 +5721,14 @@ static int accountSaveSub(reqData *Rd, inputForm *iF, inputValue *iV)
5488 Rd->stuff->putstr(Rd->stuff, "vouched", getStrH(Rd->database, "Lua.vouched")); 5721 Rd->stuff->putstr(Rd->stuff, "vouched", getStrH(Rd->database, "Lua.vouched"));
5489 Rd->stuff->putstr(Rd->stuff, "voucher", getStrH(Rd->database, "Lua.voucher")); 5722 Rd->stuff->putstr(Rd->stuff, "voucher", getStrH(Rd->database, "Lua.voucher"));
5490 5723
5491 char *lvl = getStrH(Rd->body, "level");
5492 qlisttbl_obj_t obj;
5493
5494 memset((void*)&obj, 0, sizeof(obj)); // must be cleared before call 5724 memset((void*)&obj, 0, sizeof(obj)); // must be cleared before call
5495 accountLevels->lock(accountLevels); 5725 accountLevels->lock(accountLevels);
5496 while(accountLevels->getnext(accountLevels, &obj, NULL, false) == true) 5726 while(accountLevels->getnext(accountLevels, &obj, NULL, false) == true)
5497 { 5727 {
5498 if (strcmp(lvl, (char *) obj.data) == 0) 5728 if (strcmp(lvl, (char *) obj.data) == 0)
5729 {
5499 Rd->database->putstr(Rd->database, "UserAccounts.UserLevel", obj.name); 5730 Rd->database->putstr(Rd->database, "UserAccounts.UserLevel", obj.name);
5731 }
5500 } 5732 }
5501 accountLevels->unlock(accountLevels); 5733 accountLevels->unlock(accountLevels);
5502 accountWrite(Rd); 5734 accountWrite(Rd);
@@ -5535,6 +5767,7 @@ static int accountValidateSub(reqData *Rd, inputForm *iF, inputValue *iV)
5535 Rd->stuff->putstr(Rd->stuff, "aboutMe", getStrH(Rd->database, "Lua.aboutMe")); 5767 Rd->stuff->putstr(Rd->stuff, "aboutMe", getStrH(Rd->database, "Lua.aboutMe"));
5536 Rd->stuff->putstr(Rd->stuff, "vouched", getStrH(Rd->database, "Lua.vouched")); 5768 Rd->stuff->putstr(Rd->stuff, "vouched", getStrH(Rd->database, "Lua.vouched"));
5537 Rd->stuff->putstr(Rd->stuff, "voucher", getStrH(Rd->database, "Lua.voucher")); 5769 Rd->stuff->putstr(Rd->stuff, "voucher", getStrH(Rd->database, "Lua.voucher"));
5770 Rd->stuff->putstr(Rd->stuff, "approver", getStrH(Rd->database, "Lua.approver"));
5538 Rd->shs.level = -100; 5771 Rd->shs.level = -100;
5539 Rd->database->putstr(Rd->database, "UserAccounts.UserLevel", "-100"); 5772 Rd->database->putstr(Rd->database, "UserAccounts.UserLevel", "-100");
5540 accountWrite(Rd); 5773 accountWrite(Rd);
@@ -5603,8 +5836,8 @@ d("Sub accountEditSub %s %s %s", uuid, first, last);
5603 } 5836 }
5604 else 5837 else
5605 { 5838 {
5606// check if logged in user is allowed to make these changes 5839// TODO - check if logged in user is allowed to make these changes
5607// update user record 5840// TODO - update user record
5608 } 5841 }
5609 return ret; 5842 return ret;
5610} 5843}
@@ -6457,6 +6690,10 @@ void sledjchisl_main(void)
6457 I("qLibc version: qLibc only git tags for version numbers. Sooo, 2.4.4, unless I forgot to update this."); 6690 I("qLibc version: qLibc only git tags for version numbers. Sooo, 2.4.4, unless I forgot to update this.");
6458 I("toybox version: %s", TOYBOX_VERSION); 6691 I("toybox version: %s", TOYBOX_VERSION);
6459 6692
6693 T("SledjChisl arguments %d %d", (int)toys.optflags, (int)toys.optc);
6694 for (i = 0; i < toys.optc; i++)
6695 T(" argument %d %s", i, toys.optargs[i] );
6696
6460 dbRequests = qlist(0); 6697 dbRequests = qlist(0);
6461 sigatexit(cleanup); 6698 sigatexit(cleanup);
6462 6699
@@ -6517,6 +6754,7 @@ jit library is loaded or the JIT compiler will not be activated.
6517 luaL_openlibs(L); // Load Lua libraries. 6754 luaL_openlibs(L); // Load Lua libraries.
6518 6755
6519 // Load the config scripts. 6756 // Load the config scripts.
6757// TODO - should add something like current working directory here. Gets complicated, may have to go up or down a bit.
6520 char *cPaths[] = 6758 char *cPaths[] =
6521 { 6759 {
6522 ".sledjChisl.conf.lua", 6760 ".sledjChisl.conf.lua",
@@ -6528,7 +6766,6 @@ jit library is loaded or the JIT compiler will not be activated.
6528 }; 6766 };
6529 struct stat st; 6767 struct stat st;
6530 6768
6531
6532 for (i = 0; cPaths[i]; i++) 6769 for (i = 0; cPaths[i]; i++)
6533 { 6770 {
6534 memset(toybuf, 0, sizeof(toybuf)); 6771 memset(toybuf, 0, sizeof(toybuf));
@@ -6583,6 +6820,7 @@ jit library is loaded or the JIT compiler will not be activated.
6583 D("Setting DEBUG = %d", DEBUG); 6820 D("Setting DEBUG = %d", DEBUG);
6584 if ((vd = configs->get (configs, "loadAverageInc", NULL, false)) != NULL) {loadAverageInc = *((float *) vd); D("Setting loadAverageInc = %f", loadAverageInc);} 6821 if ((vd = configs->get (configs, "loadAverageInc", NULL, false)) != NULL) {loadAverageInc = *((float *) vd); D("Setting loadAverageInc = %f", loadAverageInc);}
6585 if ((vd = configs->get (configs, "simTimeOut", NULL, false)) != NULL) {simTimeOut = (int) *((float *) vd); D("Setting simTimeOut = %d", simTimeOut);} 6822 if ((vd = configs->get (configs, "simTimeOut", NULL, false)) != NULL) {simTimeOut = (int) *((float *) vd); D("Setting simTimeOut = %d", simTimeOut);}
6823 if ((vd = configs->get (configs, "bulkSims", NULL, false)) != NULL) {bulkSims = (int) *((float *) vd); D("Setting bulkSims = %d", bulkSims);}
6586 if ((tmp = configs->getstr(configs, "scRoot", false)) != NULL) {scRoot = tmp; D("Setting scRoot = %s", scRoot);} 6824 if ((tmp = configs->getstr(configs, "scRoot", false)) != NULL) {scRoot = tmp; D("Setting scRoot = %s", scRoot);}
6587 if ((tmp = configs->getstr(configs, "scUser", false)) != NULL) {scUser = tmp; D("Setting scUser = %s", scUser);} 6825 if ((tmp = configs->getstr(configs, "scUser", false)) != NULL) {scUser = tmp; D("Setting scUser = %s", scUser);}
6588 if ((tmp = configs->getstr(configs, "Tconsole", false)) != NULL) {Tconsole = tmp; D("Setting Tconsole = %s", Tconsole);} 6826 if ((tmp = configs->getstr(configs, "Tconsole", false)) != NULL) {Tconsole = tmp; D("Setting Tconsole = %s", Tconsole);}
@@ -6609,6 +6847,7 @@ jit library is loaded or the JIT compiler will not be activated.
6609 scCache = "/var/cache/opensim_SC"; 6847 scCache = "/var/cache/opensim_SC";
6610 scData = "/var/lib/opensim_SC"; 6848 scData = "/var/lib/opensim_SC";
6611 scLog = "/var/log/opensim_SC"; 6849 scLog = "/var/log/opensim_SC";
6850 scTemp = "/tmp";
6612 } 6851 }
6613 else if (strcmp("/usr/local", scRoot) == 0) 6852 else if (strcmp("/usr/local", scRoot) == 0)
6614 { 6853 {
@@ -6620,6 +6859,7 @@ jit library is loaded or the JIT compiler will not be activated.
6620 scCache = "/var/local/cache/opensim_SC"; 6859 scCache = "/var/local/cache/opensim_SC";
6621 scData = "/var/local/lib/opensim_SC"; 6860 scData = "/var/local/lib/opensim_SC";
6622 scLog = "/var/local/log/opensim_SC"; 6861 scLog = "/var/local/log/opensim_SC";
6862 scTemp = "/tmp";
6623 } 6863 }
6624 else // A place for everything to live, like /opt/opensim_SC 6864 else // A place for everything to live, like /opt/opensim_SC
6625 { 6865 {
@@ -6642,6 +6882,7 @@ jit library is loaded or the JIT compiler will not be activated.
6642 scCache = xmprintf("%s%s/var/cache", slsh, scRoot); 6882 scCache = xmprintf("%s%s/var/cache", slsh, scRoot);
6643 scData = xmprintf("%s%s/var/lib", slsh, scRoot); 6883 scData = xmprintf("%s%s/var/lib", slsh, scRoot);
6644 scLog = xmprintf("%s%s/var/log", slsh, scRoot); 6884 scLog = xmprintf("%s%s/var/log", slsh, scRoot);
6885 scTemp = xmprintf("%s%s/tmp", slsh, scRoot);
6645 } 6886 }
6646 6887
6647 6888
@@ -6662,30 +6903,75 @@ jit library is loaded or the JIT compiler will not be activated.
6662 I("Not running inside the proper tmux server, starting it. %s == %s", eTMUX, toybuf); 6903 I("Not running inside the proper tmux server, starting it. %s == %s", eTMUX, toybuf);
6663 } 6904 }
6664 6905
6665 if (isTmux || isWeb) 6906 if (!isTmux)
6666 { 6907 { // Let's see if the proper tmux server is even running.
6667 char *d; 6908 memset(toybuf, 0, sizeof(toybuf));
6909 snprintf(toybuf, sizeof(toybuf), "%s %s/%s -q list-sessions 2>/dev/null | grep -q %s:", Tcmd, scRun, Tsocket, Tconsole);
6910 i = system(toybuf);
6911 if (WIFEXITED(i))
6912 {
6913 if (0 != WEXITSTATUS(i)) // No such session, create it.
6914 {
6915 memset(toybuf, 0, sizeof(toybuf));
6916 if (strcmp(pw->pw_name, scUser) == 0)
6917 {
6918 snprintf(toybuf, sizeof(toybuf),
6919 "%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'",
6920 Tcmd, scRun, Tsocket, Tconsole, Ttab, Tconsole, scRoot);
6921 }
6922 else
6923 {
6924 // 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.
6925 // 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.
6926 snprintf(toybuf, sizeof(toybuf),
6927 "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'",
6928 scUser, Tcmd, scRun, Tsocket, Tconsole, Ttab, Tconsole, scRoot);
6929 }
6930 i = system(toybuf);
6931 if (!WIFEXITED(i))
6932 E("tmux new-session command failed! %s", toybuf);
6933 }
6934 // Join the session.
6935 memset(toybuf, 0, sizeof(toybuf));
6936 snprintf(toybuf, sizeof(toybuf), "%s %s/%s select-window -t '%s' \\; attach-session -t '%s'", Tcmd, scRun, Tsocket, Tconsole, Tconsole);
6937 i = system(toybuf);
6938 if (!WIFEXITED(i))
6939 E("tmux attach-session command failed! %s", toybuf);
6940 goto finished;
6941 }
6942 else
6943 E("tmux list-sessions command failed! %s", toybuf);
6944 }
6668 6945
6669 // Doing this here coz at this point we should be the correct user. 6946
6670 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); 6947 // Doing this here coz at this point we should be the correct user.
6671 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); 6948 I("Making directories in %s.", scRoot);
6672 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); 6949 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);
6673 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); 6950 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);
6674 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); 6951 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);
6952 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);
6953 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);
6675// TODO - the path to scCache/sledjchisl.socket needs to be readable by the www-data group. So the FCGI socket will work. 6954// TODO - the path to scCache/sledjchisl.socket needs to be readable by the www-data group. So the FCGI socket will work.
6676// AND it needs to be group sticky on opensimsc group. So the tmux socket will work. 6955// AND it needs to be group sticky on opensimsc group. So the tmux socket will work.
6677// So currently scCache is www-data readable, and scRun is group sticky. 6956// So currently scCache is www-data readable, and scRun is group sticky.
6678 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); 6957 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);
6679 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); 6958 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);
6680 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); 6959 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);
6681 tmp = xmprintf("%s/sessions", scCache); 6960 if ((! qfile_exist(scTemp)) && (! qfile_mkdir(scTemp, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP, true))) C("Unable to create path %s", scTemp);
6682 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); 6961 tmp = xmprintf("%s/sessions", scCache);
6683 free(tmp); 6962 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);
6684 tmp = xmprintf("%s/users", scData); 6963 free(tmp);
6685 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); 6964 tmp = xmprintf("%s/users", scData);
6686 free(tmp); 6965 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);
6966 free(tmp);
6967
6968 getSims();
6687 6969
6688 6970
6971 if (isTmux || isWeb)
6972 {
6973 char *d;
6974
6689 mimeTypes = qhashtbl(0, 0); 6975 mimeTypes = qhashtbl(0, 0);
6690 mimeTypes->putstr(mimeTypes, "gz", "application/gzip"); 6976 mimeTypes->putstr(mimeTypes, "gz", "application/gzip");
6691 mimeTypes->putstr(mimeTypes, "js", "application/javascript"); 6977 mimeTypes->putstr(mimeTypes, "js", "application/javascript");
@@ -7159,111 +7445,312 @@ fcgiDone:
7159 } 7445 }
7160 7446
7161 7447
7162 if (!isTmux)
7163 { // Let's see if the proper tmux server is even running.
7164 memset(toybuf, 0, sizeof(toybuf));
7165 snprintf(toybuf, sizeof(toybuf), "%s %s/%s -q list-sessions 2>/dev/null | grep -q %s:", Tcmd, scRun, Tsocket, Tconsole);
7166 i = system(toybuf);
7167 if (WIFEXITED(i))
7168 {
7169 if (0 != WEXITSTATUS(i)) // No such sesion, create it.
7170 {
7171 memset(toybuf, 0, sizeof(toybuf));
7172 // 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.
7173 // 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.
7174 snprintf(toybuf, sizeof(toybuf),
7175 "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'",
7176 scUser, Tcmd, scRun, Tsocket, Tconsole, Ttab, Tconsole, scRoot);
7177 i = system(toybuf);
7178 if (!WIFEXITED(i))
7179 E("tmux new-session command failed! %s", toybuf);
7180 }
7181 // Join the session.
7182 memset(toybuf, 0, sizeof(toybuf));
7183 snprintf(toybuf, sizeof(toybuf), "%s %s/%s select-window -t '%s' \\; attach-session -t '%s'", Tcmd, scRun, Tsocket, Tconsole, Tconsole);
7184 i = system(toybuf);
7185 if (!WIFEXITED(i))
7186 E("tmux attach-session command failed! %s", toybuf);
7187 goto finished;
7188 }
7189 else
7190 E("tmux list-sessions command failed! %s", toybuf);
7191 }
7192 7448
7449 char *target = NULL;
7450 struct sysinfo info;
7451 float la;
7452 // 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.
7453 int cpus = (int) sysconf(_SC_NPROCESSORS_CONF), cpusOnline = (int) sysconf(_SC_NPROCESSORS_ONLN);
7454 int doWait = 1;
7455
7456 if (0 == bulkSims)
7457 bulkSims = (cpusOnline / 3) - 1;
7458 I("There are %i CPUs, %i of them are online. Doing %i sims at once.", cpus, cpusOnline, bulkSims);
7459 sysinfo(&info);
7460 la = info.loads[0]/65536.0;
7461
7462 if (0 == toys.optc)
7463 ;
7464 else if (strcmp(toys.optargs[0], "create") == 0)
7465 currentMode = CREATE;
7466 else if (strcmp(toys.optargs[0], "start") == 0)
7467 currentMode = START;
7468 else if (strcmp(toys.optargs[0], "backup") == 0)
7469 currentMode = BACKUP;
7470 else if (strcmp(toys.optargs[0], "gitar") == 0)
7471 currentMode = GITAR;
7472 else if (strcmp(toys.optargs[0], "status") == 0)
7473 currentMode = STATUS;
7474 else if (strcmp(toys.optargs[0], "stop") == 0)
7475 currentMode = STOP;
7476 else
7477 target = toys.optargs[0];
7478 if (2 == toys.optc)
7479 target = toys.optargs[1];
7480
7481T("ARGS - %i |%s| |%s|", currentMode, toys.optargs[0], target);
7193 7482
7194 simList *sims = getSims(); 7483 if ((START == currentMode) && !checkSimIsRunning("ROBUST"))
7195 if (1)
7196 { 7484 {
7197 struct sysinfo info; 7485 char *d = xmprintf("%s.{right}", Ttab);
7198 float la; 7486 char *c = xmprintf("cd %s/current/bin", scRoot);
7199 7487
7200 sysinfo(&info); 7488 I("ROBUST is starting up.");
7201 la = info.loads[0]/65536.0; 7489 sendTmuxCmd(d, c);
7490 free(c);
7491 c = xmprintf("mono Robust.exe -inidirectory=%s/config/ROBUST", scRoot);
7492 sendTmuxCmd(d, c);
7493 free(c);
7494 waitTmuxText(d, "INITIALIZATION COMPLETE FOR ROBUST");
7495 I("ROBUST is done starting up.");
7496 la = waitLoadAverage(la, loadAverageInc / 3.0, simTimeOut / 3);
7497 free(d);
7498 }
7202 7499
7203 if (!checkSimIsRunning("ROBUST")) 7500/*
7204 { 7501sims = -- Note these are .shini / tmux tab short names.
7205 char *d = xmprintf("%s.{right}", Ttab); 7502{
7206 char *c = xmprintf("cd %s/current/bin", scRoot); 7503 {["type"] = "Important"; "Welcome", "Sandbox", "Shops"},
7207 7504 {["type"] = "Rentals"; "Karen", "Bob"},
7208 I("ROBUST is starting up."); 7505 {["type"] = "Freebies"; "Gifts", "Free"},
7209 sendTmuxCmd(d, c); 7506 {["type"] = "unsorted"; "New"}, -- NOTE - this is where new ones go to by default.
7210 free(c); 7507 {["type"] = "Water"; ["number"] = 90; "Water0", "Water1", "Water2", "Water3", "Water4"},
7211 c = xmprintf("mono Robust.exe -inidirectory=%s/config/ROBUST", scRoot); 7508 {["type"] = "Heavies"; ["number"] = 70; "DP", "ARSE"},
7212 sendTmuxCmd(d, c); 7509}
7213 free(c); 7510*/
7214 waitTmuxText(d, "INITIALIZATION COMPLETE FOR ROBUST"); 7511 memset(toybuf, 0, sizeof(toybuf));
7215 I("ROBUST is done starting up."); 7512 snprintf(toybuf, sizeof(toybuf), "%s/sims.lua", scEtc);
7216 la = waitLoadAverage(la, loadAverageInc / 3.0, simTimeOut / 3); 7513 if (0 == lstat(toybuf, &st))
7217 free(d); 7514 {
7218 } 7515 int number = 1, count = 1;
7219 7516
7220 for (i = 0; i < sims->num; i++) 7517 I("Loading configuration file - %s", toybuf);
7518 status = luaL_loadfile(L, toybuf);
7519 if (status) // If something went wrong, error message is at the top of the stack.
7520 E("Couldn't load file: %s", lua_tostring(L, -1));
7521 else
7221 { 7522 {
7222 char *sim = sims->sims[i], *name = getSimName(sims->sims[i]); 7523 result = lua_pcall(L, 0, LUA_MULTRET, 0);
7223 7524 if (result)
7224 if (!checkSimIsRunning(sim)) 7525 E("Failed to run script: %s", lua_tostring(L, -1));
7526 else
7225 { 7527 {
7226 char *nm = cleanSimName(name); 7528 lua_getglobal(L, "sims");
7227 7529
7228 I("%s is starting up.", name); 7530 lua_pushnil(L); // lua_next() needs the last key at the top, but we don't start with one.
7229 memset(toybuf, 0, sizeof(toybuf)); 7531 while(lua_next(L, -2) != 0) // pushes key then value.
7230 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'", 7532 { // key -2 value -1
7231 Tcmd, scRun, Tsocket, nm, Tconsole, i + 1, scRoot, scRoot, sim); 7533 if (lua_istable(L, -1))
7232 int r = system(toybuf); 7534 {
7233 if (!WIFEXITED(r)) 7535 const char *type = "unsorted";
7234 E("tmux new-window command failed!"); 7536
7235 else 7537 lua_getfield(L, -1, "type");
7236 { 7538 lua_getfield(L, -2, "number");
7237 memset(toybuf, 0, sizeof(toybuf)); 7539 if (LUA_TNIL != lua_type(L, -2)) type = lua_tostring(L, -2);
7238 snprintf(toybuf, sizeof(toybuf), "INITIALIZATION COMPLETE FOR %s", name); 7540 if (LUA_TNIL != lua_type(L, -1)) number = lua_tonumber(L, -1);
7239 waitTmuxText(nm, toybuf); 7541 lua_pop(L, 2);
7240 I("%s is done starting up.", name); 7542
7241 la = waitLoadAverage(la, loadAverageInc, simTimeOut); 7543 T("Doing sims of type %s starting at number %d", type, number);
7544 lua_pushnil(L);
7545 while(lua_next(L, -2) != 0)
7546 {
7547 char *sim = (char *) lua_tostring(L, -1);
7548
7549 if (LUA_TNUMBER == lua_type(L, -2))
7550 {
7551 int num;
7552 char *nm = NULL;
7553 char *name;// = getSimName(ourSims->sims[i], &nm, &num);
7554 qlisttbl_t *ini = ourSims->byTab->get(ourSims->byTab, sim, NULL, false);
7555 simData *simd;
7556
7557 if (NULL == ini)
7558 {
7559 E("Sim %s not found in ini list!", sim);
7560 }
7561 else
7562 {
7563 simd = ini->get(ini, "SIM DATA", NULL, false);
7564 name = simd->name;
7565 nm = simd->tab;
7566 }
7567
7568 if (NULL != target)
7569 {
7570 int cont = TRUE;
7571
7572 memset(toybuf, 0, sizeof(toybuf));
7573 snprintf(toybuf, sizeof(toybuf), "%s.shini", nm);
7574 if
7575 (
7576 (strcmp(target, sim) == 0) ||
7577// (strcmp(target, simd->nmbr) == 0) ||
7578 (strcmp(target, name) == 0) ||
7579 (strcmp(target, nm) == 0) ||
7580 (strcmp(target, toybuf) == 0)
7581 )
7582 cont = FALSE;
7583 memset(toybuf, 0, sizeof(toybuf));
7584 snprintf(toybuf, sizeof(toybuf), "%s.ini", nm);
7585 if (strcmp(target, toybuf) == 0)
7586 cont = FALSE;
7587 memset(toybuf, 0, sizeof(toybuf));
7588 snprintf(toybuf, sizeof(toybuf), "sim%d.ini", number);
7589 if (strcmp(target, toybuf) == 0)
7590 cont = FALSE;
7591 if (cont)
7592 goto nextSim;
7593 }
7594
7595 doWait = (count % bulkSims);
7596 switch (currentMode)
7597 {
7598 case START : // "start sim01" "start 'Welcome sim'" "start Welcome.ini" "start Welcome" "start" start everything
7599 { // TODO - check if sim is down, but tmux window is still up, and close the tmux window first.
7600 if (!checkSimIsRunning(nm))
7601 {
7602 char *path = xmprintf("%s/%s.shini", scEtc, nm);
7603 char *newPath = xmprintf("%s/sim%d/%s.ini", scTemp, number, nm); // Coz OpenSim.
7604 char *cmd = xmprintf("rm -fr %s/sim%d; mkdir -p %s/sim%d; sed -E"
7605 " -e 's/InternalPort[[:space:]]*=[[:space:]]*[[:digit:]]+/InternalPort = %d/'"
7606 " -e 's/InternalPort[[:space:]]*=[[:space:]]*\"[[:digit:]]+\"/InternalPort = %d/'"
7607 " -e 's/http_listener_port[[:space:]]*=[[:space:]]*[[:digit:]]+/http_listener_port = %d/'"
7608 " -e 's/http_listener_port[[:space:]]*=[[:space:]]*\"[[:digit:]]+\"/http_listener_port = %d/'"
7609 " %s >%s",
7610 scTemp, number,
7611 scTemp, number,
7612 8004 + number * 2,
7613 8004 + number * 2,
7614 8005 + number * 2,
7615 8005 + number * 2,
7616 path, newPath);
7617 int j;
7618
7619 D("Writing .ini file %s with ports %d %d", newPath, 8004 + number * 2, 8005 + number * 2);
7620 j = system(cmd);
7621 if (!WIFEXITED(j))
7622 E("sed command failed!");
7623 free(cmd);
7624
7625 I("%s is starting up in tab [%d:%s], from %s/sim%d. %i %i", name, number, nm, scTemp, number, doWait, count);
7626 memset(toybuf, 0, sizeof(toybuf));
7627 snprintf(toybuf, sizeof(toybuf), "%s %s/%s new-window -dn '%s' -t '%s:%d' 'cd %s/current/bin; mono OpenSim.exe -inidirectory=%s/sim%d'",
7628 Tcmd, scRun, Tsocket, nm, Tconsole, number, scRoot, scTemp, number);
7629 int r = system(toybuf);
7630 if (!WIFEXITED(r))
7631 E("tmux new-window command failed!");
7632 else
7633 {
7634 if (0 == doWait)
7635 {
7636 memset(toybuf, 0, sizeof(toybuf));
7637 snprintf(toybuf, sizeof(toybuf), "INITIALIZATION COMPLETE FOR %s", name);
7638 waitTmuxText(nm, toybuf);
7639 I("%s is done starting up.", name);
7640 la = waitLoadAverage(la, loadAverageInc, simTimeOut);
7641 }
7642 else
7643 I("Not waiting for %s to finish starting up.", name);
7644 count++;
7645 }
7646 }
7647 break;
7648 }
7649
7650 case BACKUP : // "backup 'onefang rejected'" "backup sim01" "backup 'Welcome Sim'" "backup" backup everything
7651 { // TODO - If it's not a sim code, and not a sim name, it's an account inventory.
7652 if (checkSimIsRunning(nm))
7653 {
7654 struct timeval tv;
7655 time_t curtime;
7656 char date[DATE_TIME_LEN];
7657
7658
7659// TODO - should collect names of existing backups, both tmux / ini name and proper name.
7660// Scan backups directory once before this for loop, add details to sims list.
7661// strip off the last bit of file name (YYYY-mm-dd_HH:MM:SS.oar) to get the name
7662// keep in mind some old files had munged names like "Tiffanie_s_Paradise-2021-06-23_05:11:38.oar"
7663 gettimeofday(&tv, NULL);
7664 curtime = tv.tv_sec;
7665 strftime(date, DATE_TIME_LEN, "%F_%T", localtime(&curtime));
7666 I("%s is being backed up to %s/backups/%s-%s.oar.", name, scRoot, nm, date);
7667 memset(toybuf, 0, sizeof(toybuf));
7668 snprintf(toybuf, sizeof(toybuf), "save oar --all %s/backups/%s-%s.oar", scRoot, nm, date);
7669 sendTmuxCmd(nm, toybuf);
7670// if (0 == doWait)
7671 {
7672 memset(toybuf, 0, sizeof(toybuf));
7673 snprintf(toybuf, sizeof(toybuf), "Finished writing out OAR for %s", name);
7674 waitTmuxText(nm, toybuf);
7675 I("%s is done backing up.", name);
7676// TODO - should delete / gitAR the old ones now.
7677// Have a config option for delete / gitAR.
7678 la = waitLoadAverage(la, loadAverageInc, simTimeOut);
7679 }
7680 }
7681 break;
7682 }
7683
7684 case CREATE : // "create name x,y size"
7685 {
7686 break;
7687 }
7688
7689 case GITAR : // "gitAR i name" "gitAR o name"
7690 {
7691 break;
7692 }
7693
7694 case STATUS :
7695 {
7696 break;
7697 }
7698
7699 case STOP : // "stop sim01" "stop 'Welcome Sim'" "stop" stop everything
7700 {
7701 if (checkSimIsRunning(nm))
7702 {
7703 I("%s is stopping.", name);
7704 sendTmuxCmd(nm, "quit");
7705 // There's three things that might happen -
7706 // Sim will quit, tmux tab will go away before we can see the shutdown message.
7707 // Sim will quit, tmux tab wont go away. Dunno yet if waitTmuxText() will still work.
7708 // Sim fails to quit, error message on the tab that we want to read, wont see the shutdown message.
7709 // Also sim might not have finished starting up, and may even not be accepting comands yet.
7710 // TODO - cater for and test them all.
7711 // Could also loop on checkSimIsRunning(sim)
7712// memset(toybuf, 0, sizeof(toybuf));
7713// snprintf(toybuf, sizeof(toybuf), "[SHUTDOWN]: Shutdown processing on main thread complete. Exiting...");
7714// waitTmuxText(nm, toybuf);
7715// I("%s is done stopping.", name);
7716// la = waitLoadAverage(la, loadAverageInc, simTimeOut);
7717 }
7718 break;
7719 }
7720
7721 default :
7722 {
7723 E("Unknown command! %s", toys.optargs[0]);
7724 break;
7725 }
7726 }
7727nextSim:
7728 number++;
7729 }
7730 lua_pop(L, 1); // removes 'value'; keeps 'key' for next iteration
7731 }
7732 }
7733
7734 lua_pop(L, 1);
7242 } 7735 }
7243 free(nm);
7244 } 7736 }
7245 free(name);
7246 } 7737 }
7247
7248 }
7249 else if (!strcmp(cmd, "create")) // "create name x,y size"
7250 {
7251 }
7252 else if (!strcmp(cmd, "start")) // "start sim01" "start Welcome" "start" start everything
7253 {
7254// TODO - check if sim is down, but tmux window is still up, and close the tmux window first.
7255 }
7256 else if (!strcmp(cmd, "backup")) // "backup onefang rejected" "backup sim01" "backup Welcome" "backup" backup everything
7257 { // If it's not a sim code, and not a sim name, it's an account inventory.
7258 } 7738 }
7259 else if (!strcmp(cmd, "gitAR")) // "gitAR i name" 7739
7260 { 7740
7261 } 7741 if ((START == currentMode) && checkSimIsRunning("ROBUST"))
7262 else if (!strcmp(cmd, "stop")) // "stop sim01" "stop Welcome" "stop" stop everything
7263 { 7742 {
7743 // Start up the web stuff. TODO - remove this once we handle the fcgi stuff ourselves.
7744 char *d = xmprintf("%s.{left}", Ttab);
7745 char *c = xmprintf("cd %s/current/src;"
7746 " spawn-fcgi -n -u %s -s %s/sledjchisl.socket -M 0660 -G www-data -- "
7747 "/usr/bin/valgrind --leak-check=full build/toybox/generated/unstripped/toybox sledjchisl",
7748 scRoot, scUser, scCache);
7749 sendTmuxCmd(d, c);
7750 free(c);
7751 free(d);
7264 } 7752 }
7265 7753
7266 freeSimList(sims);
7267 7754
7268/* 7755/*
7269 double sum; 7756 double sum;
@@ -7328,6 +7815,7 @@ fcgiDone:
7328*/ 7815*/
7329 7816
7330finished: 7817finished:
7818 freeSimList(ourSims);
7331 7819
7332 // An example of calling a toy directly. 7820 // An example of calling a toy directly.
7333// printf("\n\n"); 7821// printf("\n\n");