From 9df93a76177822feb036e2875ad09f35627c53c3 Mon Sep 17 00:00:00 2001 From: onefang Date: Tue, 6 Jul 2021 14:12:03 +1000 Subject: 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. --- src/.sledjChisl.conf.lua | 1 + src/sledjchisl/sledjchisl.c | 898 ++++++++++++++++++++++++++++++++++---------- 2 files changed, 694 insertions(+), 205 deletions(-) (limited to 'src') diff --git a/src/.sledjChisl.conf.lua b/src/.sledjChisl.conf.lua index 6b8f29b..a817995 100644 --- a/src/.sledjChisl.conf.lua +++ b/src/.sledjChisl.conf.lua @@ -18,6 +18,7 @@ config = ["Ttab"] = "SC"; ["loadAverageInc"] = 0.7; ["simTimeOut"] = 45; -- seconds + ["bulkSims"] = 0; -- 0 means figure it out from number of CPUs. ["webRoot"] = "/var/www/html"; ["webHost"] = "localhost"; ["URL"] = "sledjchisl.fcgi"; 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 @@ * * Copyright 2020 David Seikel * Not in SUSv4. An entirely new invention, thus no web site either. + * + * Not using the usual taybox args system, coz it's not working as advertised. -USE_SLEDJCHISL(NEWTOY(sledjchisl, "m(mode):", TOYFLAG_USR|TOYFLAG_BIN)) +USE_SLEDJCHISL(NEWTOY(sledjchisl, "", TOYFLAG_USR|TOYFLAG_BIN)) config SLEDJCHISL bool "sledjchisl" default y help - usage: sledjchisl [-m|--mode mode] + usage: sledjchisl [mode [arguments]] opensim-SC management system. + + Mode selects what sledjchisl will do - + create "Sim Name" x,y size + will create a sim. + start + start sim01 + start "Welcome sim" + will start a sim or everything. + backup + backup "Joan Smith" + backup sim01 + backup "Welcome sim" + will backup sim or everything. + gitar i "Joan Smith" + gitar o sim01 + gitar o "Welcome sim" + restart + restart sim01 + restart "Welcome sim" + will stop then start a sim, or everything. + status + status sim01 + status "Welcome sim" + will show status of a sim, or everything. + stop + stop sim01 + stop "Welcome sim" + will stop a sim, or everything. */ +/* + configs/sim01 - + backup-sim + start-sim + stop-sim + +Currently they are all links to ../../current/scripts/start-sim, +they should be links to ../../current/bin/sledjchisl, +which itself is a link to ../src/build/toybox/generated/unstripped/toybox or ../src/build/toybox/toybox + +*/ // TODO - figure out how to automate testing of this. // Being all interactive and involving external web servers / viewers makes it hard. @@ -54,7 +95,7 @@ extern char **environ; // https://mariadb.com/kb/en/about-mariadb-connector-c/ Official docs. // http://dev.mysql.com/doc/refman/5.5/en/c-api-function-overview.html MySQL docs. // http://zetcode.com/db/mysqlc/ MySQL tutorial. -#include +//#include #include #include @@ -71,16 +112,18 @@ extern char **environ; // I deal with that by using a sed invokation when building toybox. #include "toys.h" +typedef enum +{ + CREATE = 0, + START = 1, + BACKUP = 2, + GITAR = 3, + RESTART = 4, + STATUS = 5, + STOP = 9 +} modes; -GLOBALS( - char *mode; -) - -#define TT this.sledjchisl - -#define FLAG_m 2 - - +modes currentMode = START; // Duplicate some small amount of code from qLibc, coz /, + and, = are not good choices, and the standard says we can pick those. /** @@ -282,10 +325,10 @@ typedef enum // Silly "man getrandom" is bullshitting. // Note - this is Linux specific, it's calling a Linux kernel function. // Remove this when we have a real getrandom(), and replace it with - -// #include +//#include #include #include -int getrandom(void *b, size_t l, unsigned int f) +ssize_t getrandom(void *b, size_t l, unsigned int f) { return (int) syscall(SYS_getrandom, b, l, f); } @@ -427,6 +470,7 @@ char *scBackup = ""; char *scCache = ""; char *scData = ""; char *scLog = ""; +char *scTemp = ""; char *Tconsole = "SledjChisl"; char *Tsocket = "opensim-tmux.socket"; char *Ttab = "SC"; @@ -439,8 +483,9 @@ int seshRenew = 10 * 60; int idleTimeOut = 30 * 60; int seshTimeOut = 24 * 60 * 60; int newbieTimeOut = 30; -float loadAverageInc = 0.5; +float loadAverageInc = 0.7; int simTimeOut = 45; +int bulkSims = 0; boolean DEBUG = TRUE; qhashtbl_t *mimeTypes; qlist_t *dbRequests; @@ -463,7 +508,7 @@ char *logTypes[] = "36", "TIMEOUT", // cyan "97;40", "INFO", // white "90", "DEBUG", // grey -// VERBOSE? UNKNOWN? FATAL? SILENT? All from Android aparently. +// VERBOSE? UNKNOWN? FATAL? SILENT? All from Android apparently. "35", "debug", // magenta "34", "timeout", // blue }; @@ -647,12 +692,38 @@ float waitLoadAverage(float la, float extra, int timeout) // Rob forget to do this, but at least he didn't declare it static. struct dirtree *dirtree_handle_callback(struct dirtree *new, int (*callback)(struct dirtree *node)); + +// A sim structure for holding all the stuff from the sim.ini file. +typedef struct _simData simData; +struct _simData +{ + // portH is the HTTP port for the sim, portI is the UDP port for the sim. + int num, locX, locY, sizeX, sizeY, sizeZ, portH, portI, maxPrims; + char *name, *tab, *UUID, *regionType, *estate, *owner; +// char *nmbr; +}; + typedef struct _simList simList; struct _simList { int len, num; char **sims; + qtreetbl_t *tbl, *byTab; }; +simList *ourSims = NULL; + +static int getIntFromIni(qlisttbl_t *ini, char *name) +{ + int ret; + char *t = "0"; + + t = ini->getstr(ini, name, false); + if (NULL == t) + t = "0"; + else if ('"' == t[0]) + t = qstrunchar(t, '"', '"'); + return strtol(t, NULL, 10); +} static int filterSims(struct dirtree *node) { @@ -672,6 +743,7 @@ static int filterSims(struct dirtree *node) return 0; } +/* // We particularly don't want \ " ` char *cleanSimName(char *name) { @@ -690,19 +762,163 @@ char *cleanSimName(char *name) return ret; } +*/ + +static int filterInis(struct dirtree *node) +{ + if (!node->parent) return DIRTREE_RECURSE | DIRTREE_SHUTUP; + int l = strlen(node->name); + if (strncmp(&(node->name[l - 4]), ".ini", 4) == 0) + { + strncpy((char *) node->parent->extra, node->name, l - 4); + return DIRTREE_ABORT; + } + return 0; +} simList *getSims() { - simList *sims = xmalloc(sizeof(simList)); - memset(sims, 0, sizeof(simList)); - char *path = xmprintf("%s/config", scRoot); + if (NULL != ourSims) return ourSims; + + char *path = xmprintf("%s/config", scRoot), *newPath; struct dirtree *new = dirtree_add_node(0, path, 0); - new->extra = (long) sims; + int i, j; + + ourSims = xmalloc(sizeof(simList)); + memset(ourSims, 0, sizeof(simList)); + + ourSims->tbl = qtreetbl(0); + ourSims->byTab = qtreetbl(0); + new->extra = (long) ourSims; dirtree_handle_callback(new, filterSims); - qsort(sims->sims, sims->num, sizeof(char *), qstrcmp); + qsort(ourSims->sims, ourSims->num, sizeof(char *), qstrcmp); free(path); - return sims; + + + char *file = xmprintf("%s/sims.lua", scEtc); + char *tnm = "sims = -- Note these are .shini / tmux tab short names.\n{\n {['type'] = 'unsorted';\n"; + struct stat st; + int s = stat(file, &st); + int fd = -1; + size_t l = strlen(tnm); + + if (s) + { + I("Creating sims %s.", file); + fd = notstdio(xcreate_stdio(file, O_CREAT | O_WRONLY | O_TRUNC | O_CLOEXEC, S_IRUSR | S_IWUSR)); + } + + if (-1 != fd) + { + if (l != writeall(fd, tnm, l)) + perror_msg("Writing %s", file); + } + + + for (i = 0; i < ourSims->num; i++) + { + char *sim = ourSims->sims[i], *name = xmprintf("%s/config/%s", scRoot, sim); + qlisttbl_t *ini; + simData *simd = xmalloc(sizeof(simData)); + + struct dirtree *new = dirtree_add_node(0, name, 0); + + free(name); + name = xzalloc(1024); + new->extra = (long) name; + dirtree_handle_callback(new, filterInis); + if ('\0' != name[0]) + { + path = xmprintf("%s/config/%s/%s.ini", scRoot, sim, name); + newPath = xmprintf("%s/%s.shini", scEtc, name); + I("Reading .ini file %s", path); + ini = qconfig_parse_file(NULL, path, '='); + +ini->putstr(ini, "INI FILE", name); +/* + [Region] + Location = "1,1" + InternalPort = "9008" + MaxPrims = 45000 + + [Network] + http_listener_port = 9007 +*/ + simd->num = getIntFromIni(ini, "Const.mysim"); + simd->name = qstrunchar(ini->getstr(ini, "Region.RegionName", false), '"', '"'); + simd->UUID = qstrunchar(ini->getstr(ini, "Region.RegionUUID", false), '"', '"'); + simd->regionType = qstrunchar(ini->getstr(ini, "Region.RegionType", false), '"', '"'); + simd->sizeX = getIntFromIni(ini, "Region.SizeX"); + simd->sizeY = getIntFromIni(ini, "Region.SizeY"); + simd->sizeZ = getIntFromIni(ini, "Region.SizeZ"); + simd->tab = name; +// simd->nmbr = sim; + ini->put(ini, "SIM DATA", simd, sizeof(simData)); +ourSims->tbl->put(ourSims->tbl, sim, ini, sizeof(qlisttbl_t)); + ourSims->byTab->put(ourSims->byTab, name, ini, sizeof(qlisttbl_t)); + + if ((!qfile_exist(newPath))) + { + char *cmd = xmprintf("sed -E" + " -e 's#\\[Const]#\\[Const] ; fakeVariableCozOpenSim='' ; pushd ../current/bin; ./sledjchisl $1 `basename $0`; popd ; exit 0#'" + " -e 's/mysim=\"[[:digit:]]*\"/mysim=\"%s\"/'" + " -e 's/sim\\$\\{Const\\|mysim\\}/\\$\\{Const\\|mysim\\}/g'" + " %s >%s", simd->tab, path, newPath); + + I("Writing .shini file %s", newPath); + D(cmd); + j = system(cmd); + if (!WIFEXITED(j)) + E("sed command failed!"); + else + { + free(cmd); + cmd = xmprintf("chmod ugo+x %s", newPath); + j = system(cmd); + if (!WIFEXITED(j)) + E("chmod command failed!"); + + + char *link = xmprintf("%s/%s.shini", scBin, simd->tab); + I("Symlinking %s to %s", newPath, link); + if (0 != symlink(newPath, link)) + perror_msg("Symlinking %s to %s", newPath, link); + free(link); + + + } + free(cmd); + } + } + free(newPath); + free(path); + + + if (-1 != fd) + { + tnm = xmprintf(" '%s', \n", simd->tab); + l = strlen(tnm); + if (l != writeall(fd, tnm, l)) + perror_msg("Writing %s", file); + free(tnm); + } + + } + + if (-1 != fd) + { + tnm = " },\n}\nreturn sims\n"; + l = strlen(tnm); + if (l != writeall(fd, tnm, l)) + perror_msg("Writing %s", file); + + xclose(fd); + } + free(file); + + + return ourSims; } void freeSimList(simList *sims) @@ -712,69 +928,44 @@ void freeSimList(simList *sims) for (i = 0; i < sims->num; i++) free(sims->sims[i]); free(sims->sims); - free(sims); -} -static int filterInis(struct dirtree *node) -{ - if (!node->parent) return DIRTREE_RECURSE | DIRTREE_SHUTUP; - int l = strlen(node->name); - if (strncmp(&(node->name[l - 4]), ".ini", 4) == 0) + qtreetbl_obj_t obj; + memset((void*) &obj, 0, sizeof(obj)); // start from the minimum. + sims->tbl->lock(sims->tbl); + while (sims->tbl->getnext(sims->tbl, &obj, false) == true) + { + char *name = qmemdup(obj.name, obj.namesize); // keep the name + size_t namesize = obj.namesize; // for removal argument + qlisttbl_t *ini = (qlisttbl_t *) obj.data; + if (NULL != ini) { - strcpy((char *) node->parent->extra, node->name); - return DIRTREE_ABORT; - } - return 0; -} + simData *simd = ini->get(ini, "SIM DATA", NULL, false); -char *getSimName(char *sim) -{ - char *ret = NULL; - char *c = xmprintf("%s/config/%s", scRoot, sim); - struct dirtree *new = dirtree_add_node(0, c, 0); - - free(c); - c = xzalloc(1024); - new->extra = (long) c; - dirtree_handle_callback(new, filterInis); - if ('\0' != c[0]) - { - char *temp = NULL; - regex_t pat; - regmatch_t m[2]; - long len; - int fd; - - temp = xmprintf("%s/config/%s/%s", scRoot, sim, c); - fd = xopenro(temp); - xregcomp(&pat, "RegionName = \"(.+)\"", REG_EXTENDED); - do - { - // TODO - get_line() is slow, and wont help much with DOS and Mac line endings. - // gio_gets() isn't any faster really, but deals with DOS line endings at least. - free(temp); - temp = get_line(fd); - if (temp) + if (NULL != simd) { - if (!regexec(&pat, temp, 2, m, 0)) - { - // Return first parenthesized subexpression as string. - if (pat.re_nsub > 0) - { - ret = xmprintf("%.*s", (int) (m[1].rm_eo - m[1].rm_so), temp + m[1].rm_so); - free(temp); - break; - } - } + free(simd->name); + free(simd->UUID); + free(simd->regionType); +// free(simd->estate); +// free(simd->owner); + free(simd); } - } while (temp); - regfree(&pat); - xclose(fd); +// TODO - this leaks memory, but it's a bug in qLibc. Send the bug fix upstream. +// It either leaks, or frees twice. Pffft +// ini->clear(ini); +// ini->free(ini); + ; + } + + sims->tbl->remove_by_obj(sims->tbl, obj.name, obj.namesize); // remove + obj = sims->tbl->find_nearest(sims->tbl, name, namesize, false); // rewind one step back + free(name); // clean up } - free(c); - return ret; -} + sims->tbl->unlock(sims->tbl); + sims->tbl->free(sims->tbl); + free(sims); +} // Expects either "simXX" or "ROBUST". int checkSimIsRunning(char *sim) @@ -3421,6 +3612,7 @@ char *checkLinky(reqData *Rd) ret = xmprintf("

You have an email waiting with a validation link in it, please check your email.   " "It will be from %s@%s, and it might be in your spam folder, coz these sorts of emails sometimes end up there.   " "You should add that email address to your contacts, or otherwise let it through your spam filter.   " + "If your email client wont let you click the validation link, just copy and paste it into your web browser.   " // "%s" "

\n", "grid_no_reply", Rd->Host, @@ -3646,15 +3838,22 @@ t("Write shs %s", tnm4); "\n" "Please go to this web link to validate your new account -\n" "https://%s%s?hashish=%s\n" + "If your email client wont let you click the validation\n" + "link, just copy and paste it into your web browser.\n" + "\n" "\n" "Do not reply to this email.\n" - "\n", + "\n" + "\n" + "A copy of the grids Terms of Service that you agreed to\n" + "when you created your account is included below.\n" + "%s", Rd->Host, Rd->Host, Rd->Host, getStrH(Rd->stuff, "email"), Rd->Host, Rd->Host, first, last, first, last, Rd->Host, Rd->RUri, - Rd->Host, Rd->RUri, t1 + Rd->Host, Rd->RUri, t1, ToS ); l = strlen(content); file = xmprintf("%s/sessions/%s.email", scCache, shs->leaf); @@ -3894,6 +4093,7 @@ static void accountWrite(reqData *Rd) if (!writeLuaString (Rd, fd, file, "aboutMe", NULL)) goto notWritten; if (!writeLuaString (Rd, fd, file, "vouched", "off")) goto notWritten; if (!writeLuaString (Rd, fd, file, "voucher", NULL)) goto notWritten; + if (!writeLuaString (Rd, fd, file, "approver", NULL)) goto notWritten; if (!writeLuaString (Rd, fd, file, "link", link)) goto notWritten; l = strlen(end); if (l != writeall(fd, end, l)) @@ -3939,7 +4139,7 @@ notWritten: "UserLevel", "UserFlags", "UserTitle", -// "ServiceURLs", // No worky "text", filled with crap. +// "ServiceURLs", // No worky "text", filled with crap. Leaving it blank works fine. "active", NULL }; @@ -4090,16 +4290,26 @@ notWritten: } // load iar -m first last / password /opt/opensim_SC/backups/DefaultMember.IAR - simList *sims = getSims(); struct sysinfo info; float la; sysinfo(&info); la = info.loads[0]/65536.0; - for (i = 0; i < sims->num; i++) + for (i = 0; i < ourSims->num; i++) { - char *sim = sims->sims[i], *name = getSimName(sims->sims[i]); + char *sim = ourSims->sims[i], *name;// = getSimName(ourSims->sims[i], &nm, &num); + + qlisttbl_t *ini = ourSims->tbl->get(ourSims->tbl, sim, NULL, false); + if (NULL == ini) + { + E("Sim %s not found in ini list!", sim); +// name = getSimName(ourSims->sims[i], &nm, &num); + } + else + name = qstrunchar(ini->getstr(ini, "Region.RegionName", false), '"', '"'); + + if (checkSimIsRunning(sim)) { @@ -4124,7 +4334,6 @@ T(c); } free(name); } - freeSimList(sims); } free(first); } @@ -4796,8 +5005,8 @@ static int DoBValidate(reqData *Rd, inputForm *iF, inputValue *iV) else { i = atoi(t0); -// TODO - get this to use current year instead of 2020. - if ((1900 > i) || (i > 2020)) +// TODO - get this to use current year instead of 2021. + if ((1900 > i) || (i > 2021)) { bitch(Rd, "Please supply a year of birth.", "Out of range."); ret++; @@ -4807,7 +5016,7 @@ static int DoBValidate(reqData *Rd, inputForm *iF, inputValue *iV) bitch(Rd, "Please supply a proper year of birth.", "Out of range, too old."); ret++; } - else if (i >2004) + else if (i >2005) { bitch(Rd, "This grid is Adult rated, you are too young.", "Out of range, too young."); ret++; @@ -5074,6 +5283,7 @@ static void accountViewWeb(reqData *Rd, inputForm *oF, inputValue *oV) *level = getStrH(Rd->database, "UserAccounts.UserLevel"), *email = getStrH(Rd->database, "UserAccounts.Email"), *voucher = getStrH(Rd->database, "Lua.voucher"), + *approver = getStrH(Rd->database, "Lua.approver"), *about = getStrH(Rd->database, "Lua.aboutMe"); time_t crtd = atol(getStrH(Rd->database, "UserAccounts.Created")); @@ -5086,6 +5296,7 @@ static void accountViewWeb(reqData *Rd, inputForm *oF, inputValue *oV) Rd->reply->addstrf(Rd->reply, "

Email : %s

", email); Rd->reply->addstrf(Rd->reply, "

UUID : %s

", getStrH(Rd->database, "UserAccounts.PrincipalID")); Rd->reply->addstrf(Rd->reply, "

Voucher : %s

", voucher); + Rd->reply->addstrf(Rd->reply, "

Approver : %s

", approver); HTMLtextArea(Rd->reply, "aboutMe", "About", 7, 50, 4, 16384, "", "off", "true", "soft", about, FALSE, TRUE); accountWebSubs(Rd, oF); accountWebFooter(Rd, oF); @@ -5097,6 +5308,7 @@ static void accountEditWeb(reqData *Rd, inputForm *oF, inputValue *oV) *level = getStrH(Rd->database, "UserAccounts.UserLevel"), *email = getStrH(Rd->database, "UserAccounts.Email"), *voucher = getStrH(Rd->database, "Lua.voucher"), + *approver = getStrH(Rd->database, "Lua.approver"), *about = getStrH(Rd->database, "Lua.aboutMe"), *lvl = getLevel(atoi(level)); short lv = atoi(level); @@ -5117,6 +5329,7 @@ static void accountEditWeb(reqData *Rd, inputForm *oF, inputValue *oV) { qlisttbl_obj_t obj; + HTMLhidden(Rd->reply, "approver", approver); HTMLselect(Rd->reply, "level", "level"); memset((void*)&obj, 0, sizeof(obj)); // must be cleared before call accountLevels->lock(accountLevels); @@ -5288,6 +5501,7 @@ T("Found Lua record."); Rd->database->putstr(Rd->database, "Lua.aboutMe", getStrH(tnm, "aboutMe")); Rd->database->putstr(Rd->database, "Lua.vouched", getStrH(tnm, "vouched")); Rd->database->putstr(Rd->database, "Lua.voucher", getStrH(tnm, "voucher")); + Rd->database->putstr(Rd->database, "Lua.approver", getStrH(tnm, "approver")); } // else if (rows) if (rows) @@ -5369,9 +5583,9 @@ static int accountDelSub(reqData *Rd, inputForm *iF, inputValue *iV) } else { -// check if logged in user is allowed to delete this account -// delete user record -// log the user out if they are logged in +// TODO - check if logged in user is allowed to delete this account +// TODO - delete user record +// TODO - log the user out if they are logged in } return ret; } @@ -5464,7 +5678,8 @@ static int accountSaveSub(reqData *Rd, inputForm *iF, inputValue *iV) { int ret = 0; // Using body[user] here, coz we got to this page via a URL query. - char *uuid = Rd->shs.UUID, *first = getStrH(Rd->body, "user"), *last = NULL; + char *uuid = Rd->shs.UUID, *first = getStrH(Rd->body, "user"), *last = NULL, + *level = getStrH(Rd->database, "UserAccounts.UserLevel"); int c = accountRead(Rd, NULL, first, last); if (1 != c) @@ -5474,6 +5689,24 @@ static int accountSaveSub(reqData *Rd, inputForm *iF, inputValue *iV) } else if ((0 == ret) && (strcmp("POST", Rd->Method) == 0)) { + char *lvl = getStrH(Rd->body, "level"); + qlisttbl_obj_t obj; + + if (strcmp(level, lvl) != 0) + { + if (200 <= Rd->shs.level) + { + I("%s %s approved by %s.", getStrH(Rd->database, "UserAccounts.FirstName"), getStrH(Rd->database, "UserAccounts.LastName"), Rd->shs.name); + Rd->stuff->putstr(Rd->stuff, "approver", Rd->shs.name); + } + else + { + bitch(Rd, "Cannot change level.", "User level not high enough."); + lvl = level; + Rd->stuff->putstr(Rd->stuff, "approver", getStrH(Rd->database, "Lua.approver")); + } + } + Rd->stuff->putstr(Rd->stuff, "email", getStrH(Rd->database, "UserAccounts.Email")); Rd->stuff->putstr(Rd->stuff, "created", getStrH(Rd->database, "UserAccounts.Created")); Rd->stuff->putstr(Rd->stuff, "flags", getStrH(Rd->database, "UserAccounts.UserFlags")); @@ -5488,15 +5721,14 @@ static int accountSaveSub(reqData *Rd, inputForm *iF, inputValue *iV) Rd->stuff->putstr(Rd->stuff, "vouched", getStrH(Rd->database, "Lua.vouched")); Rd->stuff->putstr(Rd->stuff, "voucher", getStrH(Rd->database, "Lua.voucher")); - char *lvl = getStrH(Rd->body, "level"); - qlisttbl_obj_t obj; - memset((void*)&obj, 0, sizeof(obj)); // must be cleared before call accountLevels->lock(accountLevels); while(accountLevels->getnext(accountLevels, &obj, NULL, false) == true) { if (strcmp(lvl, (char *) obj.data) == 0) + { Rd->database->putstr(Rd->database, "UserAccounts.UserLevel", obj.name); + } } accountLevels->unlock(accountLevels); accountWrite(Rd); @@ -5535,6 +5767,7 @@ static int accountValidateSub(reqData *Rd, inputForm *iF, inputValue *iV) Rd->stuff->putstr(Rd->stuff, "aboutMe", getStrH(Rd->database, "Lua.aboutMe")); Rd->stuff->putstr(Rd->stuff, "vouched", getStrH(Rd->database, "Lua.vouched")); Rd->stuff->putstr(Rd->stuff, "voucher", getStrH(Rd->database, "Lua.voucher")); + Rd->stuff->putstr(Rd->stuff, "approver", getStrH(Rd->database, "Lua.approver")); Rd->shs.level = -100; Rd->database->putstr(Rd->database, "UserAccounts.UserLevel", "-100"); accountWrite(Rd); @@ -5603,8 +5836,8 @@ d("Sub accountEditSub %s %s %s", uuid, first, last); } else { -// check if logged in user is allowed to make these changes -// update user record +// TODO - check if logged in user is allowed to make these changes +// TODO - update user record } return ret; } @@ -6457,6 +6690,10 @@ void sledjchisl_main(void) I("qLibc version: qLibc only git tags for version numbers. Sooo, 2.4.4, unless I forgot to update this."); I("toybox version: %s", TOYBOX_VERSION); + T("SledjChisl arguments %d %d", (int)toys.optflags, (int)toys.optc); + for (i = 0; i < toys.optc; i++) + T(" argument %d %s", i, toys.optargs[i] ); + dbRequests = qlist(0); sigatexit(cleanup); @@ -6517,6 +6754,7 @@ jit library is loaded or the JIT compiler will not be activated. luaL_openlibs(L); // Load Lua libraries. // Load the config scripts. +// TODO - should add something like current working directory here. Gets complicated, may have to go up or down a bit. char *cPaths[] = { ".sledjChisl.conf.lua", @@ -6528,7 +6766,6 @@ jit library is loaded or the JIT compiler will not be activated. }; struct stat st; - for (i = 0; cPaths[i]; i++) { memset(toybuf, 0, sizeof(toybuf)); @@ -6583,6 +6820,7 @@ jit library is loaded or the JIT compiler will not be activated. D("Setting DEBUG = %d", DEBUG); if ((vd = configs->get (configs, "loadAverageInc", NULL, false)) != NULL) {loadAverageInc = *((float *) vd); D("Setting loadAverageInc = %f", loadAverageInc);} if ((vd = configs->get (configs, "simTimeOut", NULL, false)) != NULL) {simTimeOut = (int) *((float *) vd); D("Setting simTimeOut = %d", simTimeOut);} + if ((vd = configs->get (configs, "bulkSims", NULL, false)) != NULL) {bulkSims = (int) *((float *) vd); D("Setting bulkSims = %d", bulkSims);} if ((tmp = configs->getstr(configs, "scRoot", false)) != NULL) {scRoot = tmp; D("Setting scRoot = %s", scRoot);} if ((tmp = configs->getstr(configs, "scUser", false)) != NULL) {scUser = tmp; D("Setting scUser = %s", scUser);} 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. scCache = "/var/cache/opensim_SC"; scData = "/var/lib/opensim_SC"; scLog = "/var/log/opensim_SC"; + scTemp = "/tmp"; } else if (strcmp("/usr/local", scRoot) == 0) { @@ -6620,6 +6859,7 @@ jit library is loaded or the JIT compiler will not be activated. scCache = "/var/local/cache/opensim_SC"; scData = "/var/local/lib/opensim_SC"; scLog = "/var/local/log/opensim_SC"; + scTemp = "/tmp"; } else // A place for everything to live, like /opt/opensim_SC { @@ -6642,6 +6882,7 @@ jit library is loaded or the JIT compiler will not be activated. scCache = xmprintf("%s%s/var/cache", slsh, scRoot); scData = xmprintf("%s%s/var/lib", slsh, scRoot); scLog = xmprintf("%s%s/var/log", slsh, scRoot); + scTemp = xmprintf("%s%s/tmp", slsh, scRoot); } @@ -6662,30 +6903,75 @@ jit library is loaded or the JIT compiler will not be activated. I("Not running inside the proper tmux server, starting it. %s == %s", eTMUX, toybuf); } - if (isTmux || isWeb) - { - char *d; + if (!isTmux) + { // Let's see if the proper tmux server is even running. + memset(toybuf, 0, sizeof(toybuf)); + snprintf(toybuf, sizeof(toybuf), "%s %s/%s -q list-sessions 2>/dev/null | grep -q %s:", Tcmd, scRun, Tsocket, Tconsole); + i = system(toybuf); + if (WIFEXITED(i)) + { + if (0 != WEXITSTATUS(i)) // No such session, create it. + { + memset(toybuf, 0, sizeof(toybuf)); + if (strcmp(pw->pw_name, scUser) == 0) + { + snprintf(toybuf, sizeof(toybuf), + "%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'", + Tcmd, scRun, Tsocket, Tconsole, Ttab, Tconsole, scRoot); + } + else + { + // 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. + // 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. + snprintf(toybuf, sizeof(toybuf), + "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'", + scUser, Tcmd, scRun, Tsocket, Tconsole, Ttab, Tconsole, scRoot); + } + i = system(toybuf); + if (!WIFEXITED(i)) + E("tmux new-session command failed! %s", toybuf); + } + // Join the session. + memset(toybuf, 0, sizeof(toybuf)); + snprintf(toybuf, sizeof(toybuf), "%s %s/%s select-window -t '%s' \\; attach-session -t '%s'", Tcmd, scRun, Tsocket, Tconsole, Tconsole); + i = system(toybuf); + if (!WIFEXITED(i)) + E("tmux attach-session command failed! %s", toybuf); + goto finished; + } + else + E("tmux list-sessions command failed! %s", toybuf); + } - // Doing this here coz at this point we should be the correct user. - 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); - 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); - 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); - 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); - 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); + + // Doing this here coz at this point we should be the correct user. + I("Making directories in %s.", scRoot); + 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); + 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); + 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); + 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); + 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); // TODO - the path to scCache/sledjchisl.socket needs to be readable by the www-data group. So the FCGI socket will work. // AND it needs to be group sticky on opensimsc group. So the tmux socket will work. // So currently scCache is www-data readable, and scRun is group sticky. - 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); - 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); - 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); - tmp = xmprintf("%s/sessions", scCache); - 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); - free(tmp); - tmp = xmprintf("%s/users", scData); - 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); - free(tmp); + 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); + 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); + 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); + 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); + tmp = xmprintf("%s/sessions", scCache); + 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); + free(tmp); + tmp = xmprintf("%s/users", scData); + 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); + free(tmp); + + getSims(); + if (isTmux || isWeb) + { + char *d; + mimeTypes = qhashtbl(0, 0); mimeTypes->putstr(mimeTypes, "gz", "application/gzip"); mimeTypes->putstr(mimeTypes, "js", "application/javascript"); @@ -7159,111 +7445,312 @@ fcgiDone: } - if (!isTmux) - { // Let's see if the proper tmux server is even running. - memset(toybuf, 0, sizeof(toybuf)); - snprintf(toybuf, sizeof(toybuf), "%s %s/%s -q list-sessions 2>/dev/null | grep -q %s:", Tcmd, scRun, Tsocket, Tconsole); - i = system(toybuf); - if (WIFEXITED(i)) - { - if (0 != WEXITSTATUS(i)) // No such sesion, create it. - { - memset(toybuf, 0, sizeof(toybuf)); - // 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. - // 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. - snprintf(toybuf, sizeof(toybuf), - "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'", - scUser, Tcmd, scRun, Tsocket, Tconsole, Ttab, Tconsole, scRoot); - i = system(toybuf); - if (!WIFEXITED(i)) - E("tmux new-session command failed! %s", toybuf); - } - // Join the session. - memset(toybuf, 0, sizeof(toybuf)); - snprintf(toybuf, sizeof(toybuf), "%s %s/%s select-window -t '%s' \\; attach-session -t '%s'", Tcmd, scRun, Tsocket, Tconsole, Tconsole); - i = system(toybuf); - if (!WIFEXITED(i)) - E("tmux attach-session command failed! %s", toybuf); - goto finished; - } - else - E("tmux list-sessions command failed! %s", toybuf); - } + char *target = NULL; + struct sysinfo info; + float la; + // 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. + int cpus = (int) sysconf(_SC_NPROCESSORS_CONF), cpusOnline = (int) sysconf(_SC_NPROCESSORS_ONLN); + int doWait = 1; + + if (0 == bulkSims) + bulkSims = (cpusOnline / 3) - 1; + I("There are %i CPUs, %i of them are online. Doing %i sims at once.", cpus, cpusOnline, bulkSims); + sysinfo(&info); + la = info.loads[0]/65536.0; + + if (0 == toys.optc) + ; + else if (strcmp(toys.optargs[0], "create") == 0) + currentMode = CREATE; + else if (strcmp(toys.optargs[0], "start") == 0) + currentMode = START; + else if (strcmp(toys.optargs[0], "backup") == 0) + currentMode = BACKUP; + else if (strcmp(toys.optargs[0], "gitar") == 0) + currentMode = GITAR; + else if (strcmp(toys.optargs[0], "status") == 0) + currentMode = STATUS; + else if (strcmp(toys.optargs[0], "stop") == 0) + currentMode = STOP; + else + target = toys.optargs[0]; + if (2 == toys.optc) + target = toys.optargs[1]; + +T("ARGS - %i |%s| |%s|", currentMode, toys.optargs[0], target); - simList *sims = getSims(); - if (1) + if ((START == currentMode) && !checkSimIsRunning("ROBUST")) { - struct sysinfo info; - float la; + char *d = xmprintf("%s.{right}", Ttab); + char *c = xmprintf("cd %s/current/bin", scRoot); - sysinfo(&info); - la = info.loads[0]/65536.0; + I("ROBUST is starting up."); + sendTmuxCmd(d, c); + free(c); + c = xmprintf("mono Robust.exe -inidirectory=%s/config/ROBUST", scRoot); + sendTmuxCmd(d, c); + free(c); + waitTmuxText(d, "INITIALIZATION COMPLETE FOR ROBUST"); + I("ROBUST is done starting up."); + la = waitLoadAverage(la, loadAverageInc / 3.0, simTimeOut / 3); + free(d); + } - if (!checkSimIsRunning("ROBUST")) - { - char *d = xmprintf("%s.{right}", Ttab); - char *c = xmprintf("cd %s/current/bin", scRoot); - - I("ROBUST is starting up."); - sendTmuxCmd(d, c); - free(c); - c = xmprintf("mono Robust.exe -inidirectory=%s/config/ROBUST", scRoot); - sendTmuxCmd(d, c); - free(c); - waitTmuxText(d, "INITIALIZATION COMPLETE FOR ROBUST"); - I("ROBUST is done starting up."); - la = waitLoadAverage(la, loadAverageInc / 3.0, simTimeOut / 3); - free(d); - } +/* +sims = -- Note these are .shini / tmux tab short names. +{ + {["type"] = "Important"; "Welcome", "Sandbox", "Shops"}, + {["type"] = "Rentals"; "Karen", "Bob"}, + {["type"] = "Freebies"; "Gifts", "Free"}, + {["type"] = "unsorted"; "New"}, -- NOTE - this is where new ones go to by default. + {["type"] = "Water"; ["number"] = 90; "Water0", "Water1", "Water2", "Water3", "Water4"}, + {["type"] = "Heavies"; ["number"] = 70; "DP", "ARSE"}, +} +*/ + memset(toybuf, 0, sizeof(toybuf)); + snprintf(toybuf, sizeof(toybuf), "%s/sims.lua", scEtc); + if (0 == lstat(toybuf, &st)) + { + int number = 1, count = 1; - for (i = 0; i < sims->num; i++) + I("Loading configuration file - %s", toybuf); + status = luaL_loadfile(L, toybuf); + if (status) // If something went wrong, error message is at the top of the stack. + E("Couldn't load file: %s", lua_tostring(L, -1)); + else { - char *sim = sims->sims[i], *name = getSimName(sims->sims[i]); - - if (!checkSimIsRunning(sim)) + result = lua_pcall(L, 0, LUA_MULTRET, 0); + if (result) + E("Failed to run script: %s", lua_tostring(L, -1)); + else { - char *nm = cleanSimName(name); + lua_getglobal(L, "sims"); - I("%s is starting up.", name); - memset(toybuf, 0, sizeof(toybuf)); - 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'", - Tcmd, scRun, Tsocket, nm, Tconsole, i + 1, scRoot, scRoot, sim); - int r = system(toybuf); - if (!WIFEXITED(r)) - E("tmux new-window command failed!"); - else - { - memset(toybuf, 0, sizeof(toybuf)); - snprintf(toybuf, sizeof(toybuf), "INITIALIZATION COMPLETE FOR %s", name); - waitTmuxText(nm, toybuf); - I("%s is done starting up.", name); - la = waitLoadAverage(la, loadAverageInc, simTimeOut); + lua_pushnil(L); // lua_next() needs the last key at the top, but we don't start with one. + while(lua_next(L, -2) != 0) // pushes key then value. + { // key -2 value -1 + if (lua_istable(L, -1)) + { + const char *type = "unsorted"; + + lua_getfield(L, -1, "type"); + lua_getfield(L, -2, "number"); + if (LUA_TNIL != lua_type(L, -2)) type = lua_tostring(L, -2); + if (LUA_TNIL != lua_type(L, -1)) number = lua_tonumber(L, -1); + lua_pop(L, 2); + + T("Doing sims of type %s starting at number %d", type, number); + lua_pushnil(L); + while(lua_next(L, -2) != 0) + { + char *sim = (char *) lua_tostring(L, -1); + + if (LUA_TNUMBER == lua_type(L, -2)) + { + int num; + char *nm = NULL; + char *name;// = getSimName(ourSims->sims[i], &nm, &num); + qlisttbl_t *ini = ourSims->byTab->get(ourSims->byTab, sim, NULL, false); + simData *simd; + + if (NULL == ini) + { + E("Sim %s not found in ini list!", sim); + } + else + { + simd = ini->get(ini, "SIM DATA", NULL, false); + name = simd->name; + nm = simd->tab; + } + + if (NULL != target) + { + int cont = TRUE; + + memset(toybuf, 0, sizeof(toybuf)); + snprintf(toybuf, sizeof(toybuf), "%s.shini", nm); + if + ( + (strcmp(target, sim) == 0) || +// (strcmp(target, simd->nmbr) == 0) || + (strcmp(target, name) == 0) || + (strcmp(target, nm) == 0) || + (strcmp(target, toybuf) == 0) + ) + cont = FALSE; + memset(toybuf, 0, sizeof(toybuf)); + snprintf(toybuf, sizeof(toybuf), "%s.ini", nm); + if (strcmp(target, toybuf) == 0) + cont = FALSE; + memset(toybuf, 0, sizeof(toybuf)); + snprintf(toybuf, sizeof(toybuf), "sim%d.ini", number); + if (strcmp(target, toybuf) == 0) + cont = FALSE; + if (cont) + goto nextSim; + } + + doWait = (count % bulkSims); + switch (currentMode) + { + case START : // "start sim01" "start 'Welcome sim'" "start Welcome.ini" "start Welcome" "start" start everything + { // TODO - check if sim is down, but tmux window is still up, and close the tmux window first. + if (!checkSimIsRunning(nm)) + { + char *path = xmprintf("%s/%s.shini", scEtc, nm); + char *newPath = xmprintf("%s/sim%d/%s.ini", scTemp, number, nm); // Coz OpenSim. + char *cmd = xmprintf("rm -fr %s/sim%d; mkdir -p %s/sim%d; sed -E" + " -e 's/InternalPort[[:space:]]*=[[:space:]]*[[:digit:]]+/InternalPort = %d/'" + " -e 's/InternalPort[[:space:]]*=[[:space:]]*\"[[:digit:]]+\"/InternalPort = %d/'" + " -e 's/http_listener_port[[:space:]]*=[[:space:]]*[[:digit:]]+/http_listener_port = %d/'" + " -e 's/http_listener_port[[:space:]]*=[[:space:]]*\"[[:digit:]]+\"/http_listener_port = %d/'" + " %s >%s", + scTemp, number, + scTemp, number, + 8004 + number * 2, + 8004 + number * 2, + 8005 + number * 2, + 8005 + number * 2, + path, newPath); + int j; + + D("Writing .ini file %s with ports %d %d", newPath, 8004 + number * 2, 8005 + number * 2); + j = system(cmd); + if (!WIFEXITED(j)) + E("sed command failed!"); + free(cmd); + + I("%s is starting up in tab [%d:%s], from %s/sim%d. %i %i", name, number, nm, scTemp, number, doWait, count); + memset(toybuf, 0, sizeof(toybuf)); + 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'", + Tcmd, scRun, Tsocket, nm, Tconsole, number, scRoot, scTemp, number); + int r = system(toybuf); + if (!WIFEXITED(r)) + E("tmux new-window command failed!"); + else + { + if (0 == doWait) + { + memset(toybuf, 0, sizeof(toybuf)); + snprintf(toybuf, sizeof(toybuf), "INITIALIZATION COMPLETE FOR %s", name); + waitTmuxText(nm, toybuf); + I("%s is done starting up.", name); + la = waitLoadAverage(la, loadAverageInc, simTimeOut); + } + else + I("Not waiting for %s to finish starting up.", name); + count++; + } + } + break; + } + + case BACKUP : // "backup 'onefang rejected'" "backup sim01" "backup 'Welcome Sim'" "backup" backup everything + { // TODO - If it's not a sim code, and not a sim name, it's an account inventory. + if (checkSimIsRunning(nm)) + { + struct timeval tv; + time_t curtime; + char date[DATE_TIME_LEN]; + + +// TODO - should collect names of existing backups, both tmux / ini name and proper name. +// Scan backups directory once before this for loop, add details to sims list. +// strip off the last bit of file name (YYYY-mm-dd_HH:MM:SS.oar) to get the name +// keep in mind some old files had munged names like "Tiffanie_s_Paradise-2021-06-23_05:11:38.oar" + gettimeofday(&tv, NULL); + curtime = tv.tv_sec; + strftime(date, DATE_TIME_LEN, "%F_%T", localtime(&curtime)); + I("%s is being backed up to %s/backups/%s-%s.oar.", name, scRoot, nm, date); + memset(toybuf, 0, sizeof(toybuf)); + snprintf(toybuf, sizeof(toybuf), "save oar --all %s/backups/%s-%s.oar", scRoot, nm, date); + sendTmuxCmd(nm, toybuf); +// if (0 == doWait) + { + memset(toybuf, 0, sizeof(toybuf)); + snprintf(toybuf, sizeof(toybuf), "Finished writing out OAR for %s", name); + waitTmuxText(nm, toybuf); + I("%s is done backing up.", name); +// TODO - should delete / gitAR the old ones now. +// Have a config option for delete / gitAR. + la = waitLoadAverage(la, loadAverageInc, simTimeOut); + } + } + break; + } + + case CREATE : // "create name x,y size" + { + break; + } + + case GITAR : // "gitAR i name" "gitAR o name" + { + break; + } + + case STATUS : + { + break; + } + + case STOP : // "stop sim01" "stop 'Welcome Sim'" "stop" stop everything + { + if (checkSimIsRunning(nm)) + { + I("%s is stopping.", name); + sendTmuxCmd(nm, "quit"); + // There's three things that might happen - + // Sim will quit, tmux tab will go away before we can see the shutdown message. + // Sim will quit, tmux tab wont go away. Dunno yet if waitTmuxText() will still work. + // Sim fails to quit, error message on the tab that we want to read, wont see the shutdown message. + // Also sim might not have finished starting up, and may even not be accepting comands yet. + // TODO - cater for and test them all. + // Could also loop on checkSimIsRunning(sim) +// memset(toybuf, 0, sizeof(toybuf)); +// snprintf(toybuf, sizeof(toybuf), "[SHUTDOWN]: Shutdown processing on main thread complete. Exiting..."); +// waitTmuxText(nm, toybuf); +// I("%s is done stopping.", name); +// la = waitLoadAverage(la, loadAverageInc, simTimeOut); + } + break; + } + + default : + { + E("Unknown command! %s", toys.optargs[0]); + break; + } + } +nextSim: + number++; + } + lua_pop(L, 1); // removes 'value'; keeps 'key' for next iteration + } + } + + lua_pop(L, 1); } - free(nm); } - free(name); } - - } - else if (!strcmp(cmd, "create")) // "create name x,y size" - { - } - else if (!strcmp(cmd, "start")) // "start sim01" "start Welcome" "start" start everything - { -// TODO - check if sim is down, but tmux window is still up, and close the tmux window first. - } - else if (!strcmp(cmd, "backup")) // "backup onefang rejected" "backup sim01" "backup Welcome" "backup" backup everything - { // If it's not a sim code, and not a sim name, it's an account inventory. } - else if (!strcmp(cmd, "gitAR")) // "gitAR i name" - { - } - else if (!strcmp(cmd, "stop")) // "stop sim01" "stop Welcome" "stop" stop everything + + + if ((START == currentMode) && checkSimIsRunning("ROBUST")) { + // Start up the web stuff. TODO - remove this once we handle the fcgi stuff ourselves. + char *d = xmprintf("%s.{left}", Ttab); + char *c = xmprintf("cd %s/current/src;" + " spawn-fcgi -n -u %s -s %s/sledjchisl.socket -M 0660 -G www-data -- " + "/usr/bin/valgrind --leak-check=full build/toybox/generated/unstripped/toybox sledjchisl", + scRoot, scUser, scCache); + sendTmuxCmd(d, c); + free(c); + free(d); } - freeSimList(sims); /* double sum; @@ -7328,6 +7815,7 @@ fcgiDone: */ finished: + freeSimList(ourSims); // An example of calling a toy directly. // printf("\n\n"); -- cgit v1.1