Commit a85f5c1b authored by Dag Erling Smørgrav's avatar Dag Erling Smørgrav

Significant rewrite of the code used to invoke the C compiler. The most

important functional change is that the source is no longer piped to the
compiler; instead, the source file name (which now ends in .c) is passed
on the command line.  This makes it *much* easier to support non-GNU
compilers.

The cc_command parameter processed by a custom function instead of sprintf;
"%s" is replaced with the source file name, "%o" is replaced with the output
file name, and "%%" is replaced with a single "%".


git-svn-id: http://www.varnish-cache.org/svn/trunk/varnish-cache@2249 d4fa192b-c00b-0410-8231-f00ffab90ce4
parent 066ecd5b
...@@ -541,14 +541,6 @@ tweak_cc_command(struct cli *cli, struct parspec *par, const char *arg) ...@@ -541,14 +541,6 @@ tweak_cc_command(struct cli *cli, struct parspec *par, const char *arg)
if (arg == NULL) { if (arg == NULL) {
cli_out(cli, "%s", mgt_cc_cmd); cli_out(cli, "%s", mgt_cc_cmd);
} else { } else {
#if defined(HAVE_FMTCHECK)
if (arg != fmtcheck(arg, "%s %s")) {
cli_out(cli,
"Parameter has dangerous %%-string expansions.");
cli_result(cli, CLIS_PARAM);
return;
}
#endif
free(mgt_cc_cmd); free(mgt_cc_cmd);
mgt_cc_cmd = strdup(arg); mgt_cc_cmd = strdup(arg);
XXXAN(mgt_cc_cmd); XXXAN(mgt_cc_cmd);
...@@ -764,16 +756,14 @@ static struct parspec parspec[] = { ...@@ -764,16 +756,14 @@ static struct parspec parspec[] = {
"2", "seconds" }, "2", "seconds" },
{ "cc_command", tweak_cc_command, { "cc_command", tweak_cc_command,
"Command used for compiling the C source code to a " "Command used for compiling the C source code to a "
"dlopen(3) loadable object.\n" "dlopen(3) loadable object. Any occurrence of %s in "
"NB: The string must contain two %s sequences which " "the string will be replaced with the source file name, "
"will be replaced by the binary and source file names " "and %o will be replaced with the output file name.\n"
"respectively.\n"
MUST_RELOAD, MUST_RELOAD,
#ifdef __APPLE__ #ifdef __APPLE__
"exec cc -dynamiclib -Wl,-undefined,dynamic_lookup -o %s -x c" "exec cc -dynamiclib -Wl,-undefined,dynamic_lookup -o %o %s"
" - < %s"
#else #else
"exec cc -nostdinc -fpic -shared -Wl,-x -o %s -x c - < %s" "exec cc -fpic -shared -Wl,-x -o %o %s"
#endif #endif
, NULL }, , NULL },
{ "max_restarts", tweak_max_restarts, { "max_restarts", tweak_max_restarts,
......
...@@ -32,13 +32,14 @@ ...@@ -32,13 +32,14 @@
*/ */
#include <sys/types.h> #include <sys/types.h>
#include <sys/wait.h>
#include <dlfcn.h> #include <dlfcn.h>
#include <fcntl.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
#include <sys/wait.h>
#ifndef HAVE_ASPRINTF #ifndef HAVE_ASPRINTF
#include "compat/asprintf.h" #include "compat/asprintf.h"
...@@ -119,7 +120,7 @@ static const char *default_vcl = ...@@ -119,7 +120,7 @@ static const char *default_vcl =
"\n" "\n"
"sub vcl_fetch {\n" "sub vcl_fetch {\n"
" if (!obj.valid) {\n" " if (!obj.valid) {\n"
" error;\n" " error obj.status;\n"
" }\n" " }\n"
" if (!obj.cacheable) {\n" " if (!obj.cacheable) {\n"
" pass;\n" " pass;\n"
...@@ -139,147 +140,154 @@ static const char *default_vcl = ...@@ -139,147 +140,154 @@ static const char *default_vcl =
" discard;\n" " discard;\n"
"}\n"; "}\n";
/*
* Prepare the compiler command line
*/
static void
mgt_make_cc_cmd(struct vsb *sb, const char *sf, const char *of)
{
int pct;
char *p;
for (p = mgt_cc_cmd, pct = 0; *p; ++p) {
if (pct) {
switch (*p) {
case 's':
vsb_cat(sb, sf);
break;
case 'o':
vsb_cat(sb, of);
break;
case '%':
vsb_putc(sb, '%');
break;
default:
vsb_putc(sb, '%');
vsb_putc(sb, *p);
break;
}
pct = 0;
} else if (*p == '%') {
pct = 1;
} else {
vsb_putc(sb, *p);
}
}
if (pct)
vsb_putc(sb, '%');
}
/*-------------------------------------------------------------------- /*--------------------------------------------------------------------
* Invoke system C compiler on source and return resulting dlfile. * Invoke system C compiler on source and return resulting dlfile.
* Errors goes in sb; * Errors goes in sb;
*/ */
static char * static char *
mgt_CallCc(const char *source, struct vsb *sb) mgt_run_cc(const char *source, struct vsb *sb)
{ {
FILE *fo, *fs; char cmdline[1024];
char sf[] = "./vcl.XXXXXXXX"; struct vsb cmdsb;
char sf[] = "./vcl.########.c";
char *of; char *of;
struct vsb *cccmd;
char buf[128]; char buf[128];
int i, j, sfd; int p[2], sfd, srclen, status;
void *p; pid_t pid;
void *dlh;
/* Create temporary C source file */ /* Create temporary C source file */
sfd = mkstemp(sf); sfd = vtmpfile(sf);
if (sfd < 0) { if (sfd < 0) {
vsb_printf(sb, vsb_printf(sb,
"Cannot open temporary source file \"%s\": %s\n", "%s(): failed to create %s: %s",
sf, strerror(errno)); __func__, sf, strerror(errno));
return (NULL); return (NULL);
} }
fs = fdopen(sfd, "r+"); srclen = strlen(source);
XXXAN(fs); if (write(sfd, source, srclen) != srclen) {
if (fputs(source, fs) < 0 || fflush(fs)) {
vsb_printf(sb, vsb_printf(sb,
"Write error to C source file: %s\n", "Failed to write C source to file: %s",
strerror(errno)); strerror(errno));
AZ(unlink(sf)); AZ(unlink(sf));
AZ(fclose(fs)); AZ(close(sfd));
return (NULL); return (NULL);
} }
rewind(fs); AZ(close(sfd));
/* Name the output shared library by brutally overwriting "./vcl." */ /* Name the output shared library by overwriting the final 'c' */
of = strdup(sf); of = strdup(sf);
XXXAN(of); XXXAN(of);
memcpy(of, "./bin", 5); of[sizeof sf - 2] = 'o';
vsb_new(&cmdsb, cmdline, sizeof cmdline, 0);
cccmd = vsb_new(NULL, NULL, 0, VSB_AUTOEXTEND); mgt_make_cc_cmd(&cmdsb, sf, of);
XXXAN(cccmd); vsb_finish(&cmdsb);
/* XXX check vsb state */
/*
* Attempt to open a pipe to the system C-compiler. if (pipe(p) < 0) {
* ------------------------------------------------ vsb_printf(sb, "%s(): pipe() failed: %s",
* __func__, strerror(errno));
* The arguments to the C-compiler must be whatever it takes to unlink(sf);
* create a dlopen(3) compatible object file named $of from a
* source file named $sf.
*
* The source code is entirely selfcontained, so options should be
* specified to prevent the C-compiler from doing any DWITYW
* processing. For GCC this amounts to at least "-nostdinc".
*
* We wrap the entire command in a 'sh -c "..." 2>&1' to get any
* errors from popen(3)'s shell redirected to our stderr as well.
*
*/
vsb_printf(cccmd, "exec /bin/sh -c \"" );
vsb_printf(cccmd, mgt_cc_cmd, of, sf);
vsb_printf(cccmd, "\" 2>&1");
vsb_finish(cccmd);
/* XXX: check that vsb is happy about cccmd */
fo = popen(vsb_data(cccmd), "r");
if (fo == NULL) {
vsb_printf(sb,
"System error: Cannot execute C-compiler: %s\n"
"\tcommand attempted: %s\n",
strerror(errno), vsb_data(cccmd));
free(of); free(of);
AZ(unlink(sf));
AZ(fclose(fs));
vsb_delete(cccmd);
return (NULL); return (NULL);
} }
if ((pid = fork()) < 0) {
/* Any output is considered fatal */ vsb_printf(sb, "%s(): fork() failed: %s",
j = 0; __func__, strerror(errno));
while (1) { close(p[0]);
i = fread(buf, 1, sizeof buf, fo); close(p[1]);
if (i == 0) unlink(sf);
break; free(of);
if (!j) { return (NULL);
vsb_printf(sb,
"System error:\n"
"C-compiler command complained.\n"
"Command attempted: %s\nMessage:\n",
vsb_data(cccmd));
j++;
}
vsb_bcat(sb, buf, i);
} }
if (pid == 0) {
i = pclose(fo); close(p[0]);
close(STDIN_FILENO);
AZ(unlink(sf)); assert(open("/dev/null", O_RDONLY) == STDIN_FILENO);
AZ(fclose(fs)); assert(dup2(p[1], STDOUT_FILENO) == STDOUT_FILENO);
assert(dup2(p[1], STDERR_FILENO) == STDERR_FILENO);
if (j == 0 && i != 0) { execl("/bin/sh", "/bin/sh", "-c", cmdline, NULL);
vsb_printf(sb, _exit(1);
"System error:\n"
"C-compiler command failed");
if (WIFEXITED(i))
vsb_printf(sb, ", exit %d", WEXITSTATUS(i));
if (WIFSIGNALED(i))
vsb_printf(sb, ", signal %d", WTERMSIG(i));
if (WCOREDUMP(i))
vsb_printf(sb, ", core dumped");
vsb_printf(sb,
"\nCommand attempted: %s\n", vsb_data(cccmd));
} }
close(p[1]);
vsb_delete(cccmd); while (read(p[0], buf, sizeof buf) > 0)
/* XXX nothing */ ;
/* If the compiler complained, or exited non-zero, fail */ close(p[0]);
if (i || j) { unlink(sf);
(void)unlink(of); /* May or may not have created file */ if (waitpid(pid, &status, 0) < 0) {
vsb_printf(sb, "%s(): waitpid() failed: %s",
__func__, strerror(errno));
unlink(of);
free(of);
return (NULL);
}
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
vsb_printf(sb, "%s(): Compiler failed", __func__);
if (WIFEXITED(status))
vsb_printf(sb, ", exit %d", WEXITSTATUS(status));
if (WIFSIGNALED(status))
vsb_printf(sb, ", signal %d", WTERMSIG(status));
if (WCOREDUMP(status))
vsb_printf(sb, ", core dumped");
unlink(of);
free(of); free(of);
return (NULL); return (NULL);
} }
/* Next, try to load the object into the management process */ /* Next, try to load the object into the management process */
p = dlopen(of, RTLD_NOW | RTLD_LOCAL); if ((dlh = dlopen(of, RTLD_NOW | RTLD_LOCAL)) == NULL) {
if (p == NULL) { vsb_printf(sb,
vsb_printf(sb, "Problem loading compiled VCL program:\n\t%s\n", "%s(): failed to load compiled VCL program: %s",
dlerror()); __func__, dlerror());
AZ(unlink(of)); unlink(of);
free(of); free(of);
return (NULL); return (NULL);
} }
/* /*
* XXX: we should look up and check the handle in the loaded * XXX: we should look up and check the handle in the loaded
* object * object
*/ */
AZ(dlclose(p)); AZ(dlclose(dlh));
return (of); return (of);
} }
...@@ -294,7 +302,7 @@ mgt_VccCompile(struct vsb *sb, const char *b, const char *e, int C_flag) ...@@ -294,7 +302,7 @@ mgt_VccCompile(struct vsb *sb, const char *b, const char *e, int C_flag)
if (csrc != NULL) { if (csrc != NULL) {
if (C_flag) if (C_flag)
(void)fputs(csrc, stdout); (void)fputs(csrc, stdout);
vf = mgt_CallCc(csrc, sb); vf = mgt_run_cc(csrc, sb);
if (C_flag && vf != NULL) if (C_flag && vf != NULL)
AZ(unlink(vf)); AZ(unlink(vf));
free(csrc); free(csrc);
...@@ -311,7 +319,7 @@ mgt_VccCompileFile(struct vsb *sb, const char *fn, int C_flag, int fd) ...@@ -311,7 +319,7 @@ mgt_VccCompileFile(struct vsb *sb, const char *fn, int C_flag, int fd)
if (csrc != NULL) { if (csrc != NULL) {
if (C_flag) if (C_flag)
fputs(csrc, stdout); fputs(csrc, stdout);
vf = mgt_CallCc(csrc, sb); vf = mgt_run_cc(csrc, sb);
if (C_flag && vf != NULL) if (C_flag && vf != NULL)
AZ(unlink(vf)); AZ(unlink(vf));
free(csrc); free(csrc);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment