Add global music player, invoicing/payment system, account subpages, mail account admin, and the qualityiron.vegas Jet 2.0 port
Jet interpreter (Tag.cpp, __call.cpp, __imap*.cpp/h, __smtpsend.cpp/h, __imapemptyfolder.cpp/h - new): assorted parser/tag fixes found across this work - a stray literal-tag-in-comment and backslash-quote parser corruption class, a dangling-pointer fix, a __call.cpp fd leak, IMAP folder-management tags, multi-account/per-host IMAP support, and a smtpsend Message-ID fix. barant.com site (jetc/barant-site/): - A site-wide persistent music player (header band, playlist/queue, stereo FFT EQ, session-persisted state) fed by new library/ ianslibrary/music song-list subpages. - /account split into info/phones/invoices subpages; a full invoicing and Square payment system (create/edit/copy/pay/email invoice); admin invoice management and mail-account management subpages. - /services split into overview/hosting/siptrunking/documents subpages, retiring the old standalone pages. - SIPPHONE.tag: a Jet 2.0 WebRTC softphone tag. - main.css/spa.js/email.js: the SPA loading indicator, the music player band styling, and assorted fixes accumulated alongside this work. qualityiron-site/: the qualityiron.vegas job/contractor/customer/ document management app, ported from its original Jet 1.4 codebase to Jet 2.0 as barant.coms APIServers second multi-tenant site. wcwc/wcwc.jet: the WCWC site Jet source (wcwc.jtc build output not included). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
905fbafa5a
commit
b8f7793773
406 files changed
+15803
-587
No files matched your search
+73
-26
@@ -7,13 +7,15 @@
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <cerrno>
|
||||
#include <cstring>
|
||||
#include <filesystem>
|
||||
|
||||
namespace jet {
|
||||
|
||||
|
||||
__call::__call(coreutils::ZString &in, coreutils::MString &parentOut, Global &global, Tag *parent, Tag *local) : Tag(in, parentOut, global, parent, local) {
|
||||
if(hasContainer && keywordDefined("input"))
|
||||
throw coreutils::Exception("call tag cannot have both input keyword and have a container.");
|
||||
throw coreutils::Exception("call tag cannot have both input keyword and have a container.");
|
||||
if(!keywordDefined("pgm"))
|
||||
throw coreutils::Exception("pgm keyword must be specified.");
|
||||
for(ix = 0; ix < 50; ++ix)
|
||||
@@ -35,48 +37,93 @@ namespace jet {
|
||||
} else
|
||||
break;
|
||||
}
|
||||
pipe(fdo);
|
||||
// pipe()/fork() both used to go unchecked here - on failure (most
|
||||
// realistically pipe() hitting the process's open-file-descriptor
|
||||
// limit, see below) fdo[] is left holding whatever garbage was
|
||||
// already on the stack/in this member array, and every use of it
|
||||
// after this point (close(), read(), and - for a failed fork() -
|
||||
// waitpid(-1, ...), which has the special POSIX meaning "wait for
|
||||
// ANY child" rather than "wait for none") operates on that garbage
|
||||
// or on an unrelated process instead of safely no-oping. That's a
|
||||
// real, confirmed mechanism for exactly the kind of sporadic
|
||||
// memory corruption this codebase has seen: closing/reading a
|
||||
// stale fd that coincidentally matches another subsystem's live
|
||||
// socket, or reaping a concurrent request's own child out from
|
||||
// under it.
|
||||
if(pipe(fdo) != 0)
|
||||
throw coreutils::Exception("call: pipe() failed - " + std::string(strerror(errno)), __FILE__, __LINE__, 500);
|
||||
pid = fork();
|
||||
if(pid < 0) {
|
||||
close(fdo[0]);
|
||||
close(fdo[1]);
|
||||
throw coreutils::Exception("call: fork() failed - " + std::string(strerror(errno)), __FILE__, __LINE__, 500);
|
||||
}
|
||||
if(pid == 0) {
|
||||
close(fdo[0]);
|
||||
dup2(fdo[1], 1);
|
||||
if(hasContainer) {
|
||||
pipe(fdi);
|
||||
if(fork() == 0) {
|
||||
close(fdi[0]);
|
||||
write(fdi[1], container.getData(), container.getLength());
|
||||
close(fdi[1]);
|
||||
exit(0);
|
||||
if(hasContainer) {
|
||||
if(pipe(fdi) == 0) {
|
||||
pid_t innerPid = fork();
|
||||
if(innerPid == 0) {
|
||||
close(fdi[0]);
|
||||
write(fdi[1], container.getData(), container.getLength());
|
||||
close(fdi[1]);
|
||||
_exit(0);
|
||||
} else if(innerPid > 0) {
|
||||
close(fdi[1]);
|
||||
dup2(fdi[0], 0);
|
||||
close(fdi[0]);
|
||||
} else {
|
||||
close(fdi[0]);
|
||||
close(fdi[1]);
|
||||
}
|
||||
}
|
||||
close(fdi[1]);
|
||||
dup2(fdi[0], 0);
|
||||
} else if(keywordDefined("input")) {
|
||||
coreutils::MString input(resolveKeyword("input"));
|
||||
pipe(fdi);
|
||||
if(fork() == 0) {
|
||||
close(fdi[0]);
|
||||
write(fdi[1], input.getData(), input.getLength());
|
||||
close(fdi[1]);
|
||||
exit(0);
|
||||
if(pipe(fdi) == 0) {
|
||||
pid_t innerPid = fork();
|
||||
if(innerPid == 0) {
|
||||
close(fdi[0]);
|
||||
write(fdi[1], input.getData(), input.getLength());
|
||||
close(fdi[1]);
|
||||
_exit(0);
|
||||
} else if(innerPid > 0) {
|
||||
close(fdi[1]);
|
||||
dup2(fdi[0], 0);
|
||||
close(fdi[0]);
|
||||
} else {
|
||||
close(fdi[0]);
|
||||
close(fdi[1]);
|
||||
}
|
||||
}
|
||||
close(fdi[1]);
|
||||
dup2(fdi[0], 0);
|
||||
}
|
||||
rc = execvpe(argv[0], argv, global.envp);
|
||||
close(fdo[1]);
|
||||
exit(errno);
|
||||
_exit(errno);
|
||||
}
|
||||
close(fdo[1]);
|
||||
if(keywordDefined("name")) {
|
||||
coreutils::MString value;
|
||||
value.read(fdo[0]);
|
||||
storeVariable(resolveKeyword("name"), value, resolveKeyword("scope"));
|
||||
} else
|
||||
out.read(fdo[0]);
|
||||
} else
|
||||
out.read(fdo[0]);
|
||||
// fdo[0] (the read end, still open in this - the parent - process)
|
||||
// was never closed here before, leaking one file descriptor on
|
||||
// every single <call> invocation for the life of the process.
|
||||
// Harmless-looking for an occasional <call>, but <call> is used
|
||||
// throughout this codebase (jq/curl parsing, mailcreate, this
|
||||
// session's own per-line-item JS-escaping on the invoice editor,
|
||||
// which alone can issue a double-digit number of calls per page
|
||||
// load) - repeated, routine use was slowly accumulating leaked
|
||||
// fds for as long as this process stayed up, eventually hitting
|
||||
// the real open-files limit and turning into exactly the pipe()
|
||||
// failures (and the undefined behavior above) this fix addresses.
|
||||
close(fdo[0]);
|
||||
waitpid(pid, &status, 0);
|
||||
if(keywordDefined("error")) {
|
||||
if(keywordDefined("error")) {
|
||||
global.variables[resolveKeyword("error")] = (status >> 8 & 255);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
Reference in new issue
Block a user