/* * Forking is a relatively cheap way to create a new client. The new * client record shares the IO descriptors. To avoid interference, we * limit children to only produce output by closing the input-side. * * If the father itself is a temporary client, let the new child depend * on the grandfather. */ Client MCforkClient(Client father) { Client son = NULL; if (father == NULL) return NULL; if (father->father != NULL) father = father->father; if ((son = MCinitClient(father->user, father->fdin, father->fdout))) { son->fdin = NULL; son->fdout = father->fdout; son->bak = NULL; son->yycur = 0; son->father = father; son->scenario = father->scenario; if (son->prompt) GDKfree(son->prompt); son->prompt = GDKstrdup(father->prompt); son->promptlength = strlen(father->prompt); /* reuse the scopes wherever possible */ if (son->nspace == 0) son->nspace = newModule(NULL, putName("child", 5)); son->nspace->outer = father->nspace->outer; } return son; }
/* * The MonetDB server uses a startup script to boot the system. * This script is an ordinary MAL program, but will mostly * consist of include statements to load modules of general interest. * The startup script is run as user Admin. */ int malBootstrap(void) { Client c; str msg, bootfile = "mal_init", s; c = MCinitClient((oid) 0, 0, 0); assert(c != NULL); c->nspace = newModule(NULL, putName("user", 4)); initLibraries(); if ( (msg = defaultScenario(c)) ) { GDKfree(msg); GDKerror("Failed to initialise default scenario"); return 0; } MSinitClientPrg(c, "user", "main"); (void) MCinitClientThread(c); s = malInclude(c, bootfile, 0); if (s != NULL) { mnstr_printf(GDKout, "!%s\n", s); GDKfree(s); return 0; } pushEndInstruction(c->curprg->def); chkProgram(c->fdout, c->nspace, c->curprg->def); if (c->curprg->def->errors) showErrors(c); s = MALengine(c); if (s) GDKfree(s); return 1; }
/* * Forking is a relatively cheap way to create a new client. The new * client record shares the IO descriptors. To avoid interference, we * limit children to only produce output by closing the input-side. * * If the father itself is a temporary client, let the new child depend * on the grandfather. */ Client MCforkClient(Client father) { Client son = NULL; str prompt; if (father == NULL) return NULL; if (father->father != NULL) father = father->father; if((prompt = GDKstrdup(father->prompt)) == NULL) return NULL; if ((son = MCinitClient(father->user, father->fdin, father->fdout))) { son->fdin = NULL; son->fdout = father->fdout; son->bak = NULL; son->yycur = 0; son->father = father; son->scenario = father->scenario; if (son->prompt) GDKfree(son->prompt); son->prompt = prompt; son->promptlength = strlen(prompt); /* reuse the scopes wherever possible */ if (son->usermodule == 0) { son->usermodule = userModule(); if(son->usermodule == 0) { MCcloseClient(son); return NULL; } } } else { GDKfree(prompt); } return son; }
void MSscheduleClient(str command, str challenge, bstream *fin, stream *fout) { char *user = command, *algo = NULL, *passwd = NULL, *lang = NULL; char *database = NULL, *s, *dbname; Client c; /* decode BIG/LIT:user:{cypher}passwordchal:lang:database: line */ /* byte order */ s = strchr(user, ':'); if (s) { *s = 0; mnstr_set_byteorder(fin->s, strcmp(user, "BIG") == 0); user = s + 1; } else { mnstr_printf(fout, "!incomplete challenge '%s'\n", user); exit_streams(fin, fout); GDKfree(command); return; } /* passwd */ s = strchr(user, ':'); if (s) { *s = 0; passwd = s + 1; /* decode algorithm, i.e. {plain}mypasswordchallenge */ if (*passwd != '{') { mnstr_printf(fout, "!invalid password entry\n"); exit_streams(fin, fout); GDKfree(command); return; } algo = passwd + 1; s = strchr(algo, '}'); if (!s) { mnstr_printf(fout, "!invalid password entry\n"); exit_streams(fin, fout); GDKfree(command); return; } *s = 0; passwd = s + 1; } else { mnstr_printf(fout, "!incomplete challenge '%s'\n", user); exit_streams(fin, fout); GDKfree(command); return; } /* lang */ s = strchr(passwd, ':'); if (s) { *s = 0; lang = s + 1; } else { mnstr_printf(fout, "!incomplete challenge, missing language\n"); exit_streams(fin, fout); GDKfree(command); return; } /* database */ s = strchr(lang, ':'); if (s) { *s = 0; database = s + 1; /* we can have stuff following, make it void */ s = strchr(database, ':'); if (s) *s = 0; } dbname = GDKgetenv("gdk_dbname"); if (database != NULL && database[0] != '\0' && strcmp(database, dbname) != 0) { mnstr_printf(fout, "!request for database '%s', " "but this is database '%s', " "did you mean to connect to monetdbd instead?\n", database, dbname); /* flush the error to the client, and abort further execution */ exit_streams(fin, fout); GDKfree(command); return; } else { str err; oid uid; sabdb *stats = NULL; Client root = &mal_clients[0]; /* access control: verify the credentials supplied by the user, * no need to check for database stuff, because that is done per * database itself (one gets a redirect) */ err = AUTHcheckCredentials(&uid, root, &user, &passwd, &challenge, &algo); if (err != MAL_SUCCEED) { mnstr_printf(fout, "!%s\n", err); exit_streams(fin, fout); GDKfree(err); GDKfree(command); return; } err = SABAOTHgetMyStatus(&stats); if (err != MAL_SUCCEED) { /* this is kind of awful, but we need to get rid of this * message */ fprintf(stderr, "!SABAOTHgetMyStatus: %s\n", err); if (err != M5OutOfMemory) GDKfree(err); mnstr_printf(fout, "!internal server error, " "please try again later\n"); exit_streams(fin, fout); GDKfree(command); return; } if (stats->locked == 1) { if (uid == 0) { mnstr_printf(fout, "#server is running in " "maintenance mode\n"); } else { mnstr_printf(fout, "!server is running in " "maintenance mode, please try again later\n"); exit_streams(fin, fout); SABAOTHfreeStatus(&stats); GDKfree(command); return; } } SABAOTHfreeStatus(&stats); c = MCinitClient(uid, fin, fout); if (c == NULL) { if ( MCshutdowninprogress()) mnstr_printf(fout, "!system shutdown in progress, please try again later\n"); else mnstr_printf(fout, "!maximum concurrent client limit reached " "(%d), please try again later\n", MAL_MAXCLIENTS); exit_streams(fin, fout); GDKfree(command); return; } /* move this back !! */ if (c->nspace == 0) { c->nspace = newModule(NULL, putName("user", 4)); c->nspace->outer = mal_clients[0].nspace->outer; } if ((s = setScenario(c, lang)) != NULL) { mnstr_printf(c->fdout, "!%s\n", s); mnstr_flush(c->fdout); GDKfree(s); c->mode = FINISHCLIENT; } if (!GDKgetenv_isyes(mal_enableflag) && (strncasecmp("sql", lang, 3) != 0 && uid != 0)) { mnstr_printf(fout, "!only the 'monetdb' user can use non-sql languages. " "run mserver5 with --set %s=yes to change this.\n", mal_enableflag); exit_streams(fin, fout); GDKfree(command); return; } } MSinitClientPrg(c, "user", "main"); GDKfree(command); /* NOTE ABOUT STARTING NEW THREADS * At this point we have conducted experiments (Jun 2012) with * reusing threads. The implementation used was a lockless array of * semaphores to wake up threads to do work. Experimentation on * Linux, Solaris and Darwin showed no significant improvements, in * most cases no improvements at all. Hence the following * conclusion: thread reuse doesn't save up on the costs of just * forking new threads. Since the latter means no difficulties of * properly maintaining a pool of threads and picking the workers * out of them, it is favourable just to start new threads on * demand. */ /* fork a new thread to handle this client */ mnstr_settimeout(c->fdin->s, 50, GDKexiting); MSserveClient(c); }