
commit
b9b8fcd563
5 changed files with 614 additions and 0 deletions
@ -0,0 +1,10 @@
|
||||
.TH SPAWN-FCGI 1 "July 20, 2008" |
||||
.SH NAME |
||||
spawn-fcgi \- spawns fastcgi processes |
||||
.SH OPTIONS |
||||
Use --help to find out ;-) |
||||
.SH AUTHOR |
||||
spawn-fcgi was part of the lighttpd project, written by Jan Kneschke. |
||||
.PP |
||||
This manual page was written by Stefan B\"uhler <stbuehler@web.de>, |
||||
for the Debian project (but may be used by others). |
@ -0,0 +1,534 @@
|
||||
|
||||
#include <glib.h> |
||||
|
||||
#include <fcntl.h> |
||||
#include <stdlib.h> |
||||
#include <string.h> |
||||
#include <sys/stat.h> |
||||
#include <sys/wait.h> |
||||
#include <unistd.h> |
||||
|
||||
#include <grp.h> |
||||
#include <pwd.h> |
||||
|
||||
#include <errno.h> |
||||
|
||||
#include <arpa/inet.h> |
||||
#include <sys/un.h> |
||||
|
||||
#include "config.h" |
||||
|
||||
#define UNUSED(x) ((void)(x)) |
||||
|
||||
#define FCGI_LISTENSOCK_FILENO 0 |
||||
|
||||
#ifndef __GNUC__ |
||||
# define __attribute__(x) /*NOTHING*/ |
||||
#endif |
||||
|
||||
|
||||
/*
|
||||
spawn-fcgi - spawns fastcgi processes |
||||
|
||||
The basic code was extracted from lighttpd (http://www.lighttpd.net/) and modified
|
||||
by Stefan Buehler in 2008 (use glib2, keep fds open and change socket ownership). |
||||
|
||||
COPYING from lighttpd: |
||||
Copyright (c) 2004, Jan Kneschke, incremental |
||||
All rights reserved. |
||||
|
||||
Redistribution and use in source and binary forms, with or without |
||||
modification, are permitted provided that the following conditions are met: |
||||
|
||||
- Redistributions of source code must retain the above copyright notice, this |
||||
list of conditions and the following disclaimer. |
||||
|
||||
- Redistributions in binary form must reproduce the above copyright notice, |
||||
this list of conditions and the following disclaimer in the documentation |
||||
and/or other materials provided with the distribution. |
||||
|
||||
- Neither the name of the 'incremental' nor the names of its contributors may |
||||
be used to endorse or promote products derived from this software without |
||||
specific prior written permission. |
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE |
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
||||
THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
|
||||
|
||||
typedef struct { |
||||
gchar *fcgiapp, **args; |
||||
in_addr_t addr; |
||||
gint port; |
||||
gchar *unixsocket; |
||||
gint php_childs; /* set env var */ |
||||
gint fork_childs; |
||||
gchar *pid_file; |
||||
gboolean no_fork; |
||||
gboolean show_version; |
||||
gboolean keep_fds, close_fds; /* keep/close STDIN/STDOUT */ |
||||
|
||||
gchar *chroot; |
||||
gchar *uid, *gid; |
||||
gchar *socketuid, *socketgid; |
||||
gint socketmode; |
||||
} options; |
||||
|
||||
struct data { |
||||
socklen_t socklen; |
||||
struct sockaddr *sockaddr; |
||||
int socket; |
||||
|
||||
gboolean i_am_root; |
||||
uid_t uid, socketuid; |
||||
gid_t gid, socketgid; |
||||
gchar *username; |
||||
|
||||
int pid_fd; |
||||
}; |
||||
|
||||
static options opts = { |
||||
NULL, NULL, |
||||
0, |
||||
0, |
||||
NULL, |
||||
5, |
||||
1, |
||||
NULL, |
||||
FALSE, |
||||
FALSE, |
||||
FALSE, FALSE, |
||||
|
||||
NULL, |
||||
NULL, NULL, |
||||
NULL, NULL, |
||||
0600 |
||||
}; |
||||
|
||||
static struct data data; |
||||
|
||||
/* only require an entry for name != NULL, otherwise a id as key is ok */ |
||||
int readpwdent(gchar *key, uid_t *uid, gid_t *gid, gchar** name) { |
||||
struct passwd *pwd; |
||||
errno = 0; |
||||
*gid = (gid_t) -1; |
||||
if (NULL == (pwd = getpwnam(key)) && NULL == (pwd = getpwuid(atoi(key)))) { |
||||
if (name == NULL && 0 < (*uid = (uid_t) atoi(key))) return 0; |
||||
g_printerr("Couldn't find passwd entry for '%s': %s\n", key, g_strerror(errno)); |
||||
return -1; |
||||
} |
||||
*uid = pwd->pw_uid; |
||||
*gid = pwd->pw_gid; |
||||
if (name) *name = g_strdup(pwd->pw_name); |
||||
return 0; |
||||
} |
||||
|
||||
int readgrpent(gchar *key, gid_t *gid) { |
||||
struct group *grp; |
||||
errno = 0; |
||||
if (NULL == (grp = getgrnam(key)) && NULL == (grp = getgrgid(atoi(key)))) { |
||||
if (0 < (*gid = (gid_t) atoi(key))) return 0; |
||||
g_printerr("Couldn't find group entry for '%s': %s\n", key, g_strerror(errno)); |
||||
return -1; |
||||
} |
||||
*gid = grp->gr_gid; |
||||
return 0; |
||||
} |
||||
|
||||
int create_sockaddr() { |
||||
if (opts.addr != 0 && opts.port == 0) { |
||||
g_printerr("Specified address without port\n"); |
||||
return -1; |
||||
} |
||||
|
||||
if (opts.port != 0 && opts.unixsocket != NULL) { |
||||
g_printerr("Either tcp:port or unix domain socket, not both\n"); |
||||
return -1; |
||||
} |
||||
|
||||
if (opts.port == 0 && opts.unixsocket == NULL) { |
||||
g_printerr("Need either tcp:port or unix domain socket\n"); |
||||
return -1; |
||||
} |
||||
|
||||
if (opts.port != 0) { |
||||
struct sockaddr_in *sin; |
||||
sin = g_malloc0(sizeof(struct sockaddr_in)); |
||||
sin->sin_family = AF_INET; |
||||
if (opts.addr) |
||||
sin->sin_addr.s_addr = opts.addr; |
||||
else |
||||
sin->sin_addr.s_addr = htonl(INADDR_ANY); |
||||
sin->sin_port = htons(opts.port); |
||||
data.sockaddr = (struct sockaddr*) sin; |
||||
data.socklen = sizeof(struct sockaddr_in); |
||||
} else { |
||||
struct sockaddr_un *sun; |
||||
gsize slen = strlen(opts.unixsocket), len = 1 + slen + (gsize) (((struct sockaddr_un *) 0)->sun_path); |
||||
sun = (struct sockaddr_un*) g_malloc0(len); |
||||
sun->sun_family = AF_UNIX; |
||||
strcpy(sun->sun_path, opts.unixsocket); |
||||
data.sockaddr = (struct sockaddr*) sun; |
||||
data.socklen = len - 1; |
||||
} |
||||
return 0; |
||||
} |
||||
|
||||
int bind_socket() { |
||||
int s, val; |
||||
|
||||
/* Check if socket is already open */ |
||||
/* TODO: should this be skippable? */ |
||||
if (-1 == (s = socket(data.sockaddr->sa_family, SOCK_STREAM, 0))) { |
||||
g_printerr("Couldn't open socket: %s\n", g_strerror(errno)); |
||||
return -1; |
||||
} |
||||
if (0 == connect(s, data.sockaddr, data.socklen)) { |
||||
close(s); |
||||
g_printerr("Socket already in use, can't spawn\n"); |
||||
return -1; |
||||
} |
||||
close(s); |
||||
|
||||
if (opts.unixsocket) unlink(opts.unixsocket); |
||||
if (-1 == (data.socket = socket(data.sockaddr->sa_family, SOCK_STREAM, 0))) { |
||||
g_printerr("Couldn't open socket: %s\n", g_strerror(errno)); |
||||
return -1; |
||||
} |
||||
|
||||
val = 1; |
||||
if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)) < 0) { |
||||
close(s); |
||||
g_printerr("Couldn't set SO_REUSEADDR: %s\n", g_strerror(errno)); |
||||
return -1; |
||||
} |
||||
if (-1 == bind(s, data.sockaddr, data.socklen)) { |
||||
close(s); |
||||
g_printerr("Couldn't bind socket: %s\n", g_strerror(errno)); |
||||
return -1; |
||||
} |
||||
|
||||
if (-1 == listen(s, 1024)) { |
||||
close(s); |
||||
g_printerr("Couldn't listen on socket: %s\n", g_strerror(errno)); |
||||
return -1; |
||||
} |
||||
|
||||
if (opts.unixsocket) { |
||||
data.socketuid = (uid_t) -1; |
||||
data.socketgid = (gid_t) -1; |
||||
if (opts.socketuid |
||||
&& 0 != (readpwdent(opts.socketuid, &data.socketuid, &data.socketgid, NULL))) { |
||||
return -1; |
||||
} |
||||
if (opts.socketgid |
||||
&& 0 != (readgrpent(opts.socketgid, &data.socketgid))) { |
||||
return -1; |
||||
} |
||||
if (-1 == chown(opts.unixsocket, data.socketuid, data.socketgid)) { |
||||
close(s); |
||||
g_printerr("Couldn't chown socket: %s\n", g_strerror(errno)); |
||||
return -1; |
||||
} |
||||
|
||||
if (-1 == chmod(opts.unixsocket, opts.socketmode)) { |
||||
close(s); |
||||
g_printerr("Couldn't chmod socket: %s\n", g_strerror(errno)); |
||||
return -1; |
||||
} |
||||
} |
||||
|
||||
data.socket = s; |
||||
return 0; |
||||
} |
||||
|
||||
int open_pidfile() { |
||||
if (opts.pid_file) { |
||||
struct stat st; |
||||
if (0 == (data.pid_fd = open(opts.pid_file, O_WRONLY | O_CREAT | O_EXCL | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH))) { |
||||
return 0; |
||||
} |
||||
|
||||
if (errno != EEXIST) { |
||||
g_printerr("Opening pid-file '%s' failed: %s\n", opts.pid_file, g_strerror(errno)); |
||||
return -1; |
||||
} |
||||
|
||||
if (0 != stat(opts.pid_file, &st)) { |
||||
g_printerr("Stating pid-file '%s' failed: %s\n", opts.pid_file, g_strerror(errno)); |
||||
return -1; |
||||
} |
||||
|
||||
if (!S_ISREG(st.st_mode)) { |
||||
g_printerr("pid-file exists and isn't regular file: '%s'\n", opts.pid_file); |
||||
return -1; |
||||
} |
||||
|
||||
if (-1 == (data.pid_fd = open(opts.pid_file, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH))) { |
||||
g_printerr("Opening pid-file '%s' failed: %s\n", opts.pid_file, g_strerror(errno)); |
||||
return -1; |
||||
} |
||||
} |
||||
return 0; |
||||
} |
||||
|
||||
void prepare_env() { |
||||
GString *tmp; |
||||
if (-1 != opts.php_childs) { |
||||
tmp = g_string_sized_new(0); |
||||
g_string_printf(tmp, "PHP_FCGI_CHILDREN=%d", opts.php_childs); |
||||
putenv(tmp->str); |
||||
g_string_free(tmp, FALSE); |
||||
} |
||||
} |
||||
|
||||
int drop_priv() { |
||||
if (data.i_am_root) { |
||||
/* set user and group */ |
||||
|
||||
data.uid = (uid_t) -1; |
||||
data.gid = (gid_t) -1; |
||||
data.username = NULL; |
||||
|
||||
if (opts.uid |
||||
&& 0 != (readpwdent(opts.uid, &data.uid, &data.gid, &data.username))) { |
||||
return -1; |
||||
} |
||||
if (opts.gid |
||||
&& 0 != (readgrpent(opts.gid, &data.gid))) { |
||||
return -1; |
||||
} |
||||
|
||||
/* do the change before we do the chroot() */ |
||||
if (data.gid != (gid_t) -1) { |
||||
if (0 != setgid(data.gid)) { |
||||
g_printerr("setgid failed: %s\n", strerror(errno)); |
||||
return -1; |
||||
} |
||||
if (0 != setgroups(0, NULL)) { |
||||
g_printerr("setgroups failed: %s\n", strerror(errno)); |
||||
return -1; |
||||
} |
||||
|
||||
if (data.username |
||||
&& 0 != initgroups(data.username, data.gid)) { |
||||
g_printerr("initgroups failed: %s\n", strerror(errno)); |
||||
return -1; |
||||
} |
||||
} |
||||
|
||||
if (opts.chroot) { |
||||
if (-1 == chroot(opts.chroot)) { |
||||
g_printerr("chroot failed: %s\n", strerror(errno)); |
||||
return -1; |
||||
} |
||||
if (-1 == chdir("/")) { |
||||
g_printerr("chdir failed: %s\n", strerror(errno)); |
||||
return -1; |
||||
} |
||||
} |
||||
|
||||
/* drop root privs */ |
||||
if (data.uid != (uid_t) -1 |
||||
&& 0 != setuid(data.uid)) { |
||||
g_printerr("setuid failed: %s\n", strerror(errno)); |
||||
return -1; |
||||
} |
||||
} |
||||
return 0; |
||||
} |
||||
|
||||
void move2fd(int srcfd, int dstfd) { |
||||
if (srcfd != dstfd) { |
||||
close(dstfd); |
||||
dup2(srcfd, dstfd); |
||||
close(srcfd); |
||||
} |
||||
} |
||||
|
||||
void move2devnull(int fd) { |
||||
move2fd(open("/dev/null", O_RDWR), fd); |
||||
} |
||||
|
||||
pid_t daemonize() { |
||||
pid_t child; |
||||
int status; |
||||
struct timeval tv = { 0, 100 * 1000 }; |
||||
|
||||
switch (child = fork()) { |
||||
case 0: |
||||
/* loose control terminal */ |
||||
setsid(); |
||||
|
||||
return 0; |
||||
case -1: |
||||
g_printerr("Fork failed: %s\n", g_strerror(errno)); |
||||
return (pid_t) -1; |
||||
default: |
||||
/* wait */ |
||||
select(0, NULL, NULL, NULL, &tv); |
||||
|
||||
waitforchild: |
||||
switch (waitpid(child, &status, WNOHANG)) { |
||||
case 0: |
||||
g_printerr("Child spawned successfully: PID: %d\n", child); |
||||
return child; |
||||
case -1: |
||||
if (EINTR == errno) goto waitforchild; |
||||
g_printerr("Unknown error: %s\n", g_strerror(errno)); |
||||
return (pid_t) -1; |
||||
default: |
||||
if (WIFEXITED(status)) { |
||||
g_printerr("Child exited with: %d, %s\n", WEXITSTATUS(status), strerror(WEXITSTATUS(status))); |
||||
} else if (WIFSIGNALED(status)) { |
||||
g_printerr("Child signaled: %d\n", WTERMSIG(status)); |
||||
} else { |
||||
g_printerr("Child died somehow: %d\n", status); |
||||
} |
||||
return (pid_t) -1; |
||||
} |
||||
} |
||||
} |
||||
|
||||
void start() __attribute__((noreturn)); |
||||
void start() { |
||||
int save_err_fileno = -1; |
||||
move2fd(data.socket, FCGI_LISTENSOCK_FILENO); |
||||
|
||||
if (opts.close_fds) { |
||||
move2devnull(STDOUT_FILENO); |
||||
|
||||
save_err_fileno = dup(STDERR_FILENO); |
||||
fcntl(save_err_fileno, F_SETFD, FD_CLOEXEC); |
||||
|
||||
close(STDERR_FILENO); |
||||
dup2(STDOUT_FILENO, STDERR_FILENO); |
||||
} |
||||
|
||||
if (opts.args) { |
||||
execv(opts.args[0], opts.args); |
||||
} else { |
||||
GString *tmp = g_string_sized_new(0); |
||||
g_string_printf(tmp, "exec %s", opts.fcgiapp); |
||||
execl("/bin/sh", "sh", "-c", tmp->str, (char *)NULL); |
||||
} |
||||
|
||||
if (opts.close_fds) { |
||||
dup2(save_err_fileno, STDERR_FILENO); |
||||
} |
||||
g_printerr("Exec failed: %s\n", g_strerror(errno)); |
||||
exit(errno); |
||||
} |
||||
|
||||
gboolean option_parse_address(const gchar *option_name, const gchar *value, gpointer d, GError **error) { |
||||
UNUSED(option_name); |
||||
UNUSED(d); |
||||
UNUSED(error); |
||||
|
||||
opts.addr = inet_addr(value); |
||||
return TRUE; |
||||
} |
||||
|
||||
static const GOptionEntry entries[] = { |
||||
{ "application", 'f', 0, G_OPTION_ARG_FILENAME, &opts.fcgiapp, "Filename of the fcgi-application", "fcgiapp" }, |
||||
{ "address", 'a', 0, G_OPTION_ARG_CALLBACK, (gpointer) (intptr_t) &option_parse_address, "Bind to ip address", "addr" }, |
||||
{ "port", 'p', 0, G_OPTION_ARG_INT, &opts.port, "Bind to tcp-port", "port" }, |
||||
{ "socket", 's', 0, G_OPTION_ARG_FILENAME, &opts.unixsocket, "Bind to unix-domain socket", "path" }, |
||||
{ "socket-uid", 'U', 0, G_OPTION_ARG_STRING, &opts.socketuid, "change unix-domain socket owner to user-id", "user" }, |
||||
{ "socket-gid", 'G', 0, G_OPTION_ARG_STRING, &opts.socketgid, "change unix-domain socket group to group-id", "group" }, |
||||
{ "socket-mode", 'M', 0, G_OPTION_ARG_INT, &opts.socketmode, "change unix-domain socket mode", "mode" }, |
||||
{ "phpchilds", 'C', 0, G_OPTION_ARG_INT, &opts.php_childs, "(PHP only) Number of childs to spawn (default 5)", "childs" }, |
||||
{ "childs", 'F', 0, G_OPTION_ARG_INT, &opts.fork_childs, "Number of childs to fork (default 1)", "childs" }, |
||||
{ "pid", 'P', 0, G_OPTION_ARG_FILENAME, &opts.pid_file, "Name of PID-file for spawned process", "path" }, |
||||
{ "no-daemon", 'n', 0, G_OPTION_ARG_NONE, &opts.no_fork, "Don't fork (for daemontools)", NULL }, |
||||
{ "keep-fds", 0, 0, G_OPTION_ARG_NONE, &opts.keep_fds, "Keep stdout/stderr open (default for --no-daemon)", NULL }, |
||||
{ "close-fds", 0, 0, G_OPTION_ARG_NONE, &opts.close_fds, "Close stdout/stderr (default it not --no-daemon)", NULL }, |
||||
{ "version", 'v', 0, G_OPTION_ARG_NONE, &opts.show_version, "Show version", NULL }, |
||||
{ "chroot", 'c', 0, G_OPTION_ARG_FILENAME, &opts.chroot, "(root only) chroot to directory", "dir" }, |
||||
{ "uid", 'u', 0, G_OPTION_ARG_STRING, &opts.uid, "(root only) change to user-id", "user" }, |
||||
{ "gid", 'g', 0, G_OPTION_ARG_STRING, &opts.gid, "(root only) change to group-id", "group" }, |
||||
{ G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_STRING_ARRAY, &opts.args, "<fcgiapp> [fcgi app arguments]", NULL }, |
||||
{ NULL, 0, 0, 0, NULL, NULL, NULL } |
||||
}; |
||||
|
||||
int main(int argc, char **argv) { |
||||
GOptionContext *context; |
||||
GError *error = NULL; |
||||
int res; |
||||
GString *tmp = g_string_sized_new(0); |
||||
|
||||
/* init */ |
||||
data.socket = -1; |
||||
data.pid_fd = -1; |
||||
|
||||
data.i_am_root = (getuid() == 0); |
||||
/* UID handling */ |
||||
if (!data.i_am_root && (geteuid() == 0 || getegid() == 0)) { |
||||
/* we are setuid-root */ |
||||
g_printerr("Are you nuts ? Don't apply a SUID bit to this binary\n"); |
||||
return -1; |
||||
} |
||||
|
||||
context = g_option_context_new("-- <fcgiapp> [fcgi app arguments]"); |
||||
g_option_context_add_main_entries(context, entries, NULL); |
||||
g_option_context_set_summary(context, PACKAGE_NAME "-" PACKAGE_VERSION " - spawns fastcgi processes"); |
||||
|
||||
opts.close_fds = opts.no_fork ? opts.close_fds : !opts.keep_fds; |
||||
|
||||
if (!g_option_context_parse (context, &argc, &argv, &error)) { |
||||
g_printerr("Option parsing failed: %s\n", error->message); |
||||
return -1; |
||||
} |
||||
|
||||
if (opts.show_version) { |
||||
g_printerr(PACKAGE_NAME "-" PACKAGE_VERSION " - spawns fastcgi processes\n"); |
||||
g_printerr("Build: " PACKAGE_BUILD_DATE "\n"); |
||||
return 0; |
||||
} |
||||
|
||||
/* Check options */ |
||||
if ((opts.fcgiapp && opts.args) || (!opts.fcgiapp && !opts.args)) { |
||||
g_printerr("Specify either a fcgi application with -f or the application with arguments after '--'\n"); |
||||
return -1; |
||||
} |
||||
|
||||
if (0 != (res = create_sockaddr())) return res; |
||||
if (0 != (res = bind_socket())) return res; |
||||
if (0 != (res = open_pidfile())) return res; |
||||
if (0 != (res = drop_priv())) return res; |
||||
|
||||
prepare_env(); |
||||
|
||||
if (opts.no_fork) { |
||||
start(); |
||||
} else { |
||||
gint i; |
||||
for (i = 0; i < opts.fork_childs; i++) { |
||||
pid_t child = daemonize(); |
||||
if (child == (pid_t) -1) return -1; |
||||
if (child == 0) start(); |
||||
/* write pid file */ |
||||
if (data.pid_fd != -1) { |
||||
g_string_printf(tmp, "%d\n", child); |
||||
if (i == opts.fork_childs-1) { |
||||
/* avoid eol for the last one */ |
||||
write(data.pid_fd, tmp->str, tmp->len-1); |
||||
} else { |
||||
write(data.pid_fd, tmp->str, tmp->len); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
g_string_free(tmp, TRUE); |
||||
return 0; |
||||
} |
Binary file not shown.
@ -0,0 +1,65 @@
|
||||
#! /usr/bin/env python |
||||
# encoding: utf-8 |
||||
|
||||
import Options, types, sys, Runner |
||||
from time import gmtime, strftime, timezone |
||||
|
||||
# the following two variables are used by the target "waf dist" |
||||
VERSION='1.0' |
||||
APPNAME='spawn-fcgi' |
||||
|
||||
# these variables are mandatory ('/' are converted automatically) |
||||
srcdir = '.' |
||||
blddir = 'build' |
||||
|
||||
def set_options(opt): |
||||
opt.tool_options('compiler_cc') |
||||
|
||||
def tolist(x): |
||||
if type(x) is types.ListType: |
||||
return x |
||||
return [x] |
||||
|
||||
def PKGCONFIG(conf, name, uselib = None, define = '', version = '', mandatory = 0): |
||||
if not uselib: uselib = name |
||||
hconf = conf.create_pkgconfig_configurator() |
||||
hconf.name = name |
||||
hconf.version = version |
||||
hconf.uselib_store = uselib |
||||
hconf.define = define |
||||
hconf.mandatory = mandatory |
||||
res = hconf.run() |
||||
return res |
||||
|
||||
def configure(conf): |
||||
opts = Options.options |
||||
|
||||
conf.check_tool('compiler_cc') |
||||
|
||||
conf.define("PACKAGE_NAME", APPNAME) |
||||
conf.define("PACKAGE_VERSION", VERSION) |
||||
conf.define("PACKAGE_BUILD_DATE", strftime("%b %d %Y %H:%M:%S UTC", gmtime())); |
||||
|
||||
common_ccflags = [ |
||||
'-std=gnu99', '-Wall', '-g', '-Wshadow', '-W', '-pedantic', |
||||
'-fPIC', |
||||
'-DHAVE_CONFIG_H', '-D_GNU_SOURCE', |
||||
] |
||||
conf.env['CCFLAGS_spawnfcgi'] += common_ccflags |
||||
|
||||
PKGCONFIG(conf, "glib-2.0", uselib = 'glib', mandatory = 1) |
||||
incdir = conf.env['CPPPATH_glib'][0] |
||||
conf.env['CPPPATH_glib'] += [ incdir+'/glib-2.0/', incdir + '/glib-2.0/include/' ] |
||||
# CHECK_INCLUDE_FILES(conf, "glib.h", "HAVE_GLIB_H", uselib = 'glib', use = ['glib'], mandatory = 1) |
||||
|
||||
conf.write_config_header('config.h') |
||||
|
||||
def build(bld): |
||||
spawnfcgi = bld.new_task_gen('cc', 'program') |
||||
spawnfcgi.name = 'spawn-fcgi' |
||||
spawnfcgi.source = ''' |
||||
spawn-fcgi.c |
||||
''' |
||||
spawnfcgi.target = 'spawn-fcgi' |
||||
spawnfcgi.uselib += 'glib spawnfcgi' |
||||
spawnfcgi.includes = '.' |
Loading…
Reference in new issue