static void
html_encode_file(string_ty *fn)
{
int fd;
fd = open(fn->str_text, O_RDONLY);
if (fd >= 0)
{
for (;;)
{
int n;
char buffer[1025];
n = read(fd, buffer, sizeof(buffer) - 1);
if (n <= 0)
break;
buffer[n] = 0;
html_encode_charstar(buffer);
}
close(fd);
}
}
void
fork_and_watch(void)
{
int child;
int pfd[2];
int n;
int fd;
int a;
int b;
int c;
int status;
string_ty *stdout_filename;
string_ty *stderr_filename;
trace(("fork_and_watch()\n{\n"));
stdout_filename = os_edit_filename(0);
stderr_filename = os_edit_filename(0);
os_become_must_not_be_active();
//
// fork to get a process to do the command
//
switch (child = fork())
{
case -1:
http_fatal(http_error_internal_server, "fork");
// NOTREACHED
default:
//
// The parent process waits
//
break;
case 0:
//
// We are about to break stdin, stdout and sterr. These file
// descriptors are used by programs to determine the terminal
// size. Set the appropriate environment variables to that the
// sub-program agrees with Aegis.
//
env_set_page();
//
// Redirect stderr.
//
fd = creat(stderr_filename->str_text, 0666);
if (fd < 0)
nfatal("open %s", stderr_filename->str_text);
if (close(2))
nfatal("close stderr");
n = dup(fd);
if (n < 0)
nfatal("%s: %d: dup", __FILE__, __LINE__);
if (n != 2)
fatal_raw("dup gave %d, not 2 (bug)", n);
close(fd);
//
// Redirect stdout.
//
fd = creat(stdout_filename->str_text, 0600);
if (fd < 0)
nfatal("open %s", stdout_filename->str_text);
if (close(1))
nfatal("close stdout");
n = dup(fd);
if (n < 0)
nfatal("%s: %d: dup", __FILE__, __LINE__);
if (n != 1)
fatal_raw("dup gave %d, not 1 (bug)", n);
close(fd);
//
// Redirect stdin from a broken pipe.
//
if (pipe(pfd))
nfatal("%s: %d: pipe", __FILE__, __LINE__);
if (close(0))
nfatal("%s: %d: close stdin", __FILE__, __LINE__);
n = dup(pfd[0]);
if (n < 0)
nfatal("%s: %d: dup", __FILE__, __LINE__);
if (n != 0)
fatal_raw("dup gave %d, not 0 (bug)", n);
if (close(pfd[0]) || close(pfd[1]))
nfatal("close pipe ends");
//
// The child returns to the caller.
// (The parent will watch it.)
//
return;
}
if (os_waitpid(child, &status))
http_fatal(http_error_internal_server, "wait: %s", strerror(errno));
a = (status >> 8) & 0xFF;
b = (status >> 7) & 1;
c = status & 0x7F;
switch (c)
{
case 0x7F:
//
// process was stopped,
// since we didn't do it, treat it as an error
//
http_fatal(http_error_internal_server, "command stopped");
// NOTREACHED
case 0:
// normal termination
break;
default:
//
// process dies from unhandled condition
//
if (!http_fatal_noerror)
printf("Status: 500 Internal server error\n");
html_header(0, change::pointer());
printf("Error\n");
html_header_ps(0, change::pointer());
printf("Error
\n");
printf("The request failed because:\n");
printf("command terminated by %s", safe_strsignal(c));
if (b)
printf(" (core dumped)");
printf("\n\n");
printf("The following error message was produced:\n
");
html_encode_file(stderr_filename);
printf("
\n");
html_footer(0, change::pointer());
quit(0);
}
if (a != 0)
{
//
// For errors, we wrap up stderr and send it to them.
//
if (!http_fatal_noerror)
printf("Status: 500 Internal server error\n");
html_header(0, change::pointer());
printf("Error\n");
html_header_ps(0, change::pointer());
printf("Error
\n");
printf("The request failed with exit status %d.\n", a);
printf("The following error message was produced:\n");
html_encode_file(stderr_filename);
printf("
\n");
html_footer(0, change::pointer());
}
else
{
FILE *fp;
long len;
int nl;
//
// If there is no error, we insert a Content-Length header.
//
fp = fopen(stdout_filename->str_text, "rb");
if (!fp)
{
http_fatal
(
http_error_internal_server,
"open \"%s\": %s",
stdout_filename->str_text,
strerror(errno)
);
}
fseek(fp, 0L, SEEK_END);
len = ftell(fp);
rewind(fp);
nl = 0;
for (;;)
{
c = fgetc(fp);
if (c == EOF)
break;
--len;
if (c != '\n')
nl = 0;
else
{
++nl;
if (nl == 2)
{
if (len > 0)
printf("Content-Length: %ld\n", (long)len);
putchar('\n');
break;
}
}
putchar(c);
}
for (;;)
{
char buffer[1 << 13];
n = fread(buffer, 1, sizeof(buffer), fp);
if (n <= 0)
break;
fwrite(buffer, n, 1, stdout);
}
fflush(stdout);
fclose(fp);
}
unlink(stdout_filename->str_text);
unlink(stderr_filename->str_text);
trace(("quit(0);\n"));
trace(("}\n"));
quit(0);
// NOTREACHED
}
// vim: set ts=8 sw=4 et :