Browse Source

[angel] rewrite config handling, rename items, document it

* remove "instance { ... }" wrapping
  * use "_" instead of "-"
  * modules -> modules_path
  * allow_listen { ip "..."; } -> allow_listen_ip "...";
  * allow_listen { unix "..."; } -> allow_listen_unix "...";
personal/stbuehler/wip
Stefan Bühler 8 years ago
parent
commit
22d186cecc
  1. 19
      contrib/angel.conf
  2. 109
      doc/compile.rb
  3. 248
      doc/core_config_angel.xml
  4. 52
      doc/doc_schema.xsd
  5. 19
      include/lighttpd/angel_plugin.h
  6. 30
      include/lighttpd/angel_plugin_core.h
  7. 9
      src/angel/angel_config_parser.rl
  8. 74
      src/angel/angel_plugin.c
  9. 645
      src/angel/angel_plugin_core.c
  10. 4
      src/angel/angel_server.c
  11. 24
      tests/base.py

19
contrib/angel.conf

@ -1,9 +1,14 @@
instance {
user "www-data";
max-open-files 16384;
user "www-data";
max_open_files 16384;
copy-env ( "PATH" );
copy_env [ "PATH" ];
# env [ "G_SLICE=always-malloc", "G_DEBUG=gc-friendly" ];
# wrapper [ "/usr/bin/valgrind", "--leak-check=full", "--show-reachable=yes", "--leak-resolution=high" ];
# env ( "G_SLICE=always-malloc", "G_DEBUG=gc-friendly" );
# wrapper ("/usr/bin/valgrind", "--leak-check=full", "--show-reachable=yes", "--leak-resolution=high" );
}
# need separate statements for IPv4 and IPv6. if none are configured, allow port 80 and 443 on all IPv4 and IPv6.
# no port means 80 and 443 are allowed:
# allow_listen_ip "0.0.0.0/0";
# allow_listen_ip "[::/0]";
# allow_listen_ip "0.0.0.0/0:8080";
# allow_listen_ip "[::/0]:8080";

109
doc/compile.rb

@ -249,17 +249,24 @@ class Documentation
end
end
class ModuleDocumentation < Documentation
class GenericModuleDocumentation < Documentation
def initialize(filename, xml)
super(File.basename(filename, '.xml'))
self.title = basename unless self.title
self.title = basename
render_main { _parse_module(xml.root) }
store_toc
end
def link(html_builder)
html_builder.a({:href => self.filename + '#' + escape_anchor(self.basename)}, self.title)
end
def short
@short
end
def _parse_short(xmlParent, makeDiv = false)
return unless xmlParent
xml = xmlParent.xpath('d:short[1]', XPATH_NAMESPACES)[0]
@ -306,14 +313,25 @@ class ModuleDocumentation < Documentation
}
end
def _parse_aso(xml, type)
def _parse_example(xml)
nest(xml['title'] || 'Example', xml['anchor'], 'example') {
_parse_description(xml)
config = xml.xpath('d:config[1]', XPATH_NAMESPACES)
_parse_code(config[0])
}
end
def _parse_item(xml, type, anchor_prefix = nil, title_suffix = nil)
name = xml['name']
raise "#{type} requires a name" unless name
parameter_names = xml.xpath('d:parameter', XPATH_NAMESPACES).map { |p| p['name'] }
parameter_names = ['value'] if parameter_names.length == 0 and type == 'option'
title = "#{name} (#{type})"
anchor = "#{type}_#{name}"
anchor_prefix ||= "#{type}_"
title_suffix ||= " (#{type})"
title = "#{name}#{title_suffix}"
anchor = "#{anchor_prefix}#{name}"
cls = "aso #{type}"
short = nil
@ -355,25 +373,24 @@ class ModuleDocumentation < Documentation
[name, filename + '#' + anchor, short, self]
end
end
class ModuleDocumentation < GenericModuleDocumentation
def initialize(filename, xml)
super(filename, xml)
end
def _parse_action(xml)
@actions << _parse_aso(xml, 'action')
@actions << _parse_item(xml, 'action')
end
def _parse_setup(xml)
@setups << _parse_aso(xml, 'setup')
@setups << _parse_item(xml, 'setup')
end
def _parse_option(xml)
@options << _parse_aso(xml, 'option')
end
def _parse_example(xml)
nest(xml['title'] || 'Example', xml['anchor'], 'example') {
_parse_description(xml)
config = xml.xpath('d:config[1]', XPATH_NAMESPACES)
_parse_code(config[0])
}
@options << _parse_item(xml, 'option')
end
def _parse_section(xml)
@ -418,13 +435,59 @@ class ModuleDocumentation < Documentation
end
}
end
end
def short
@short
class AngelModuleDocumentation < GenericModuleDocumentation
def initialize(filename, xml)
@items = []
super(filename, xml)
end
def link(html_builder)
html_builder.a({:href => self.filename + '#' + escape_anchor(self.basename)}, self.basename)
def _parse_item(xml)
@items << super(xml, 'item', '', '')
end
def _parse_section(xml)
title = xml['title']
raise 'section requires a title' unless title
nest(title, xml['anchor'] || '#', 'section') {
xml.children.each do |child|
if child.text?
text = child.content.strip
@html.p text if text.length > 0
elsif ['item','html','textile','markdown','example','section'].include? child.name
self.send('_parse_' + child.name, child)
else
raise 'invalid section element ' + child.name
end
end
}
end
def _parse_module(xml)
raise 'unexpected root node' if xml.name != 'angel-module'
self.title = xml['title'] || self.title
self.ordername = xml['order']
nest(title, '', 'module') {
@html.p {
@html.text (basename + ' ')
@short = _parse_short(xml, false)
}
_parse_description(xml)
xml.element_children.each do |child|
if ['item','example','section'].include? child.name
self.send('_parse_' + child.name, child)
elsif ['short', 'description'].include? child.name
nil # skip
else
raise 'invalid module element ' + child.name
end
end
}
end
end
@ -599,6 +662,8 @@ def loadXML(filename)
if xml.root.name == 'module'
ModuleDocumentation.new(filename, xml)
elsif xml.root.name == 'angel-module'
AngelModuleDocumentation.new(filename, xml)
elsif xml.root.name == 'chapter'
ChapterDocumentation.new(filename, xml)
end

248
doc/core_config_angel.xml

@ -0,0 +1,248 @@
<?xml version="1.0" encoding="UTF-8"?>
<angel-module xmlns="urn:lighttpd.net:lighttpd2/doc1" title="Angel Configuration">
<description>
<textile>
lighttpd2 consists of two main binaries: the angel (@lighttpd2@) and the worker (@lighttpd2-worker@). The "main configuration":core_config.html#core_config is used by the worker, and this chapter describes the configuration for the angel.
A standard distribution should install a angel config with reasonable defaults which should work for most basic setups.
</textile>
</description>
<section title="Angel concept">
<textile>
You can start the worker without the angel, but the angel provides some useful features:
* The angel itself usually runs as root (needed for example to bind to privileged ports), but will spawn the worker with dropped privileges (usually a user like @www-data@ is used). The worker doesn't do any privilege dropping itself.
* The angel can open/create log files for the worker with root permissions
* The angel supports a graceful restart of the worker for config reloading: a new instance is spawned, and if it started successfully (checking config, ...) it will replace the old instance. The old instance will finish the remaining requests.
As the angel is responsible for creating the listening network sockets, it can keep them open all the time and no request is lost.
* The angel also does a simple supervise: if the worker crashes the angel will respawn it.
</textile>
</section>
<section title="Config items">
<textile>
The config syntax is very similar to the "main configuration":core_config, although it has no action blocks, setup blocks, conditionals and scopes.
</textile>
<item name="user">
<short>drops privileges for spawning the worker</short>
<parameter name="username">
<short>username to drop privileges to for spawning the worker</short>
</parameter>
<description>
<textile>
This item can only be specified once; if it is not specified it won't drop privileges at all, which is useful if the angel itself doesn't run as root. It should go without saying that you should never run the worker as root.
The username is also used to find all groups the user is in.
</textile>
</description>
<example>
<config>
user "www-data";
</config>
</example>
</item>
<item name="group">
<short>drops privileges for spawning the worker</short>
<parameter name="groupname">
<short>groupname to drop privileges to for spawning the worker</short>
</parameter>
<description>
<textile>
Specify the main group to drop privileges to; a process can have multiple groups, and the others are given by the groups the user specified by @user@ is in.
The default is the main group of the user specified by @user@, or not dropping privileges at all.
</textile>
</description>
<example>
<config>
group "www-data";
</config>
</example>
</item>
<item name="binary">
<short>specifies path to worker binary</short>
<parameter name="path">
<short>path to the @lighttpd2-worker@ binary</short>
</parameter>
<description>
<textile>
This item should only be needed if you didn't install the binaries at all (for testing).
</textile>
</description>
<example>
<config>
binary "/home/source/lighttpd2/autobuild/src/main/lighttpd2-worker";
</config>
</example>
</item>
<item name="config">
<short>specifies path to main config file</short>
<parameter name="path">
<short>path to the main config file</short>
</parameter>
<description>
<textile>
By default @/etc/lighttpd2/lighttpd.conf@ is used.
</textile>
</description>
<example>
<config>
config "/etc/lighttpd2-test/lighttpd.conf";
</config>
</example>
</item>
<item name="luaconfig">
<short>specifies path to a lua config file</short>
<parameter name="path">
<short>path to the lua config file</short>
</parameter>
<description>
<textile>
By default a normal config file is used; you must use either a normal config file or a lua config file.
</textile>
</description>
<example>
<config>
luaconfig "/etc/lighttpd2/lighttpd.lua";
</config>
</example>
</item>
<item name="modules_path">
<short>specifies path to directory containing modules for the worker</short>
<parameter name="path">
<short>path to the directory containing modules for the worker</short>
</parameter>
<description>
<textile>
This item should only be needed if you didn't install the binaries at all (for testing). For autotool builds the "real" module binaries are in a @.libs@ subdirectory.
</textile>
</description>
<example>
<config>
modules_path "/home/source/lighttpd2/autobuild/src/modules/.libs";
</config>
</example>
</item>
<item name="wrapper">
<short>prefix worker command with other commands</short>
<parameter name="wrappers">
<short>path to a wrapper command and its arguments</short>
</parameter>
<description>
<textile>
This item appends all given strings to the comannd prefix list (which starts as empty list). Before spawning the worker the binary path to the worker and its arguments (config, module path) are appended.
Wrappers can be used to run the worker with valgrind, strace and similar.
</textile>
</description>
<example>
<config>
# in multiple lines
wrapper [ "/usr/bin/valgrind" ];
wrapper [ "--leak-check=full", "--show-reachable=yes" ]
wrapper [ "--leak-resolution=high" ];
# or as one
wrapper [ "/usr/bin/valgrind", "--leak-check=full", "--show-reachable=yes", "--leak-resolution=high" ];
</config>
</example>
</item>
<item name="env">
<short>add environment variables for the worker</short>
<parameter name="vars">
<short>list of environment variables to add for the worker to run with</short>
</parameter>
<description>
<textile><![CDATA[
Append the given list of environment variables (starts empty), which can be either strings of the form @"var=xyz"@ or key-value pairs @"var" => "xyz"@ (the keys must not contain any @=@).
]]></textile>
</description>
<example>
<config>
# helps debugging with valgrind:
env [ "G_SLICE=always-malloc", "G_DEBUG=gc-friendly,fatal_criticals" ];
</config>
</example>
</item>
<item name="copy_env">
<short>copies environment variables for the worker from current environment</short>
<parameter name="varnames">
<short>list of environment variable names to copy</short>
</parameter>
<description>
<textile>
Adds copies of variables from the current environment. By default all variables will be dropped.
</textile>
</description>
<example>
<config>
env_copy [ "PATH" ];
</config>
</example>
</item>
<item name="max_core_file_size">
<short>sets limit of core file size for the worker</short>
<parameter name="limit">
<short>limit in bytes</short>
</parameter>
<description>
Maximum size of a core file, in bytes, that may be created by the worker. Core files are created when the worker crashes.
0 disables core files, and by default the limit is not changed.
</description>
</item>
<item name="max_open_files">
<short>sets limit of maximum open file for the worker</short>
<parameter name="limit">
<short>maximum number of open files</short>
</parameter>
<description>
The worker limits the maximum number of connection based on the maximum number of open files (max connections = max open files / 4).
By default the limit is not changed.
</description>
<example>
<config>
# max 4096 connections
max_open_files 16384;
</config>
</example>
</item>
<item name="allow_listen_ip">
<short>allow worker to listen on a TCP socket</short>
<parameter name="mask">
<short>network mask (CIDR) + optional port</short>
</parameter>
<description>
The worker uses the angel to bind TCP sockets; the angel checks whether those binds are allowed. If no @allow_listen_ip@ or @allow_listen_unix@ is specified, all TCP binds (IPv4 and IPv6) using port 80 or 443 are allowed.
IPv4 and IPv6 use different masks (no IPv4 to IPv6 mapping), the network length for the CIDR mask is optional (defaulting to a host address), and the port is optional too (allowing both 80 and 443 if omitted).
</description>
<example>
<description>
Only allow port 8080 for IPv4 and IPv6.
</description>
<config>
allow_listen_ip "0.0.0.0/0:8080";
allow_listen_ip "[::/0]:8080";
</config>
</example>
</item>
<item name="allow_listen_unix">
<short>allow worker to listen on a unix socket</short>
<parameter name="path">
<short>path for a unix socket</short>
</parameter>
<description>
The path can contain wildcards.
</description>
</item>
</section>
</angel-module>

52
doc/doc_schema.xsd

@ -7,6 +7,7 @@
<!-- root nodes -->
<element name="module" type="d:ModuleType" />
<element name="angel-module" type="d:AngelModuleType" />
<element name="chapter" type="d:ChapterType" />
<complexType name="ModuleType">
@ -167,4 +168,55 @@
<attribute name="anchor" type="token" use="optional" />
</complexType>
<complexType name="AngelModuleType">
<sequence>
<!-- a module requires a short description (not yet, only have "angel_plugin_core") -->
<!-- <element name="short" type="string" /> -->
<!-- optional description to print below ToC -->
<element name="description" type="d:DescriptionType" minOccurs="0" />
<!-- a module cannot contain more simple text; include it in a section -->
<choice minOccurs="0" maxOccurs="unbounded">
<element name="item" type="d:AngelItemType" />
<element name="example" type="d:ExampleType" />
<element name="section" type="d:AngelModuleSectionType" />
</choice>
</sequence>
<!-- optional title, defaults to module name, which is the basename -->
<attribute name="title" type="token" use="optional" />
<!-- order modules / chapters. defaults to name -->
<attribute name="order" type="Name" use="optional" />
</complexType>
<complexType name="AngelItemType">
<sequence>
<element name="short" type="string" />
<element name="parameter" type="d:ActionSetupParameterType" minOccurs="0" maxOccurs="unbounded" />
<element name="description" type="d:DescriptionType" minOccurs="0" />
<element name="example" type="d:ExampleType" minOccurs="0" maxOccurs="unbounded" />
</sequence>
<attribute name="name" type="Name" use="required" />
</complexType>
<complexType name="AngelModuleSectionType" mixed="true">
<sequence>
<choice minOccurs="0" maxOccurs="unbounded">
<element name="item" type="d:AngelItemType" />
<element name="html" type="anyType" />
<element name="textile" type="anyType" />
<element name="markdown" type="anyType" />
<element name="example" type="d:ExampleType" />
<element name="section" type="d:AngelModuleSectionType" />
</choice>
</sequence>
<!-- a section requires a title -->
<attribute name="title" type="token" use="required" />
<!-- set anchor (id attribute of <h*> node); set to '#' to generate a unique anchor; if no anchor is set no ToC entry is created -->
<attribute name="anchor" type="token" use="optional" />
</complexType>
</schema>

19
include/lighttpd/angel_plugin.h

@ -6,7 +6,6 @@
#endif
typedef struct liPluginItem liPluginItem;
typedef struct liPluginItemOption liPluginItemOption;
typedef struct liPlugin liPlugin;
typedef struct liPlugins liPlugins;
@ -14,30 +13,18 @@ typedef gboolean (*liPluginInitCB) (liServer *srv, liPlugin *p);
typedef void (*liPluginFreeCB) (liServer *srv, liPlugin *p);
typedef void (*liPluginCleanConfigCB) (liServer *srv, liPlugin *p);
typedef gboolean (*liPluginCheckConfigCB) (liServer *srv, liPlugin *p);
typedef gboolean (*liPluginCheckConfigCB) (liServer *srv, liPlugin *p, GError **err);
typedef void (*liPluginActivateConfigCB) (liServer *srv, liPlugin *p);
typedef void (*liPluginParseItemCB) (liServer *srv, liPlugin *p, liValue **options);
typedef gboolean (*liPluginParseItemCB) (liServer *srv, liPlugin *p, liValue *value, GError **err);
typedef void (*liPluginHandleCallCB) (liServer *srv, liPlugin *p, liInstance *i, gint32 id, GString *data);
typedef void (*liPluginInstanceReplacedCB) (liServer *srv, liPlugin *p, liInstance *oldi, liInstance *newi);
typedef void (*liPluginInstanceReachedStateCB)(liServer *srv, liPlugin *p, liInstance *i, liInstanceState s);
typedef enum {
LI_PLUGIN_ITEM_OPTION_MANDATORY = 1
} liPluginItemOptionFlags;
struct liPluginItemOption {
const gchar *name; /**< name of the option */
liValueType type; /**< type of the option; may be LI_VALUE_NONE to accept anything */
liPluginItemOptionFlags flags; /**< flags of the option */
};
struct liPluginItem {
const gchar *name;
liPluginParseItemCB handle_parse_item;
const liPluginItemOption *options;
};
struct liPlugin {
@ -78,7 +65,7 @@ LI_API void li_plugins_clear(liServer *srv);
LI_API void li_plugins_config_clean(liServer *srv);
LI_API gboolean li_plugins_config_load(liServer *srv, const gchar *filename);
LI_API gboolean li_plugins_handle_item(liServer *srv, GString *itemname, liValue *hash);
LI_API gboolean li_plugins_handle_item(liServer *srv, GString *itemname, liValue *parameters, GError **err);
/* "core" is a reserved module name for interal use */
LI_API gboolean li_plugins_load_module(liServer *srv, const gchar *name);

30
include/lighttpd/angel_plugin_core.h

@ -3,12 +3,34 @@
#include <lighttpd/angel_base.h>
typedef struct liPluginCoreParsing liPluginCoreParsing;
struct liPluginCoreParsing {
GPtrArray *env; /* <gchar*> */
GString *user;
uid_t user_uid;
gid_t user_gid;
GString *group;
gid_t group_gid;
GString *binary;
GString *config;
GString *luaconfig;
GString *modules_path;
GPtrArray *wrapper; /* <gchar*> */
gint64 rlim_core, rlim_nofile;
liInstanceConf *instconf;
GPtrArray *listen_masks;
};
typedef struct liPluginCoreConfig liPluginCoreConfig;
struct liPluginCoreConfig {
/* Load */
gboolean load_failed;
liInstanceConf *load_instconf;
GPtrArray *load_listen_masks;
/* Parsing/Load */
liPluginCoreParsing parsing;
/* Running */
liInstanceConf *instconf;

9
src/angel/angel_config_parser.rl

@ -646,6 +646,7 @@ error:
static gboolean p_config_call(GString *name, liConfigTokenizerContext *ctx, GError **error) {
liValue *parameters = NULL;
gboolean res;
if (!p_parameter_values(&parameters, ctx, error)) return FALSE;
@ -657,14 +658,10 @@ static gboolean p_config_call(GString *name, liConfigTokenizerContext *ctx, GErr
return TRUE;
}
if (!li_plugins_handle_item(ctx->srv, name, parameters)) {
parse_error(ctx, error, "handling config item '%s' failed", name->str);
li_value_free(parameters);
return FALSE;
}
res = li_plugins_handle_item(ctx->srv, name, parameters, error);
li_value_free(parameters);
return TRUE;
return res;
}
/* parse actions until EOF (if !block) or '}' (if block, TK_CURLY_CLOSE) */

74
src/angel/angel_plugin.c

@ -8,7 +8,6 @@
typedef struct server_item server_item;
struct server_item {
liPlugin *p;
guint option_count;
const liPluginItem *p_item;
};
@ -27,11 +26,7 @@ static void _server_item_free(gpointer p) {
static server_item* server_item_new(liPlugin *p, const liPluginItem *p_item) {
server_item *si = g_slice_new(server_item);
const liPluginItemOption *pio;
guint cnt;
for (pio = p_item->options, cnt = 0; pio->name; pio++, cnt++) ;
si->p = p;
si->option_count = cnt;
si->p_item = p_item;
return si;
}
@ -157,8 +152,9 @@ gboolean li_plugins_config_load(liServer *srv, const gchar *filename) {
for (i = ps->load_plugins->len; i-- > 0; ) {
liPlugin *p = g_ptr_array_index(ps->load_plugins, i);
if (p->handle_check_config) {
if (!p->handle_check_config(srv, p)) {
ERROR(srv, "%s", "config check failed");
if (!p->handle_check_config(srv, p, &error)) {
ERROR(srv, "config check failed: %s", error->message);
g_error_free(error);
li_plugins_config_clean(srv);
return FALSE;
}
@ -201,14 +197,14 @@ gboolean li_plugins_config_load(liServer *srv, const gchar *filename) {
return TRUE;
}
gboolean li_plugins_handle_item(liServer *srv, GString *itemname, liValue *hash) {
gboolean li_plugins_handle_item(liServer *srv, GString *itemname, liValue *parameters, GError **err) {
liPlugins *ps = &srv->plugins;
server_item *si;
#if 0
/* debug items */
{
GString *tmp = li_value_to_string(hash);
GString *tmp = li_value_to_string(parameters);
ERROR(srv, "Item '%s': %s", itemname->str, tmp->str);
g_string_free(tmp, TRUE);
}
@ -216,64 +212,12 @@ gboolean li_plugins_handle_item(liServer *srv, GString *itemname, liValue *hash)
si = g_hash_table_lookup(ps->load_items, itemname->str);
if (!si) {
WARNING(srv, "Unknown item '%s' - perhaps you forgot to load the module? (ignored)", itemname->str);
g_set_error(err, LI_ANGEL_CONFIG_PARSER_ERROR, LI_ANGEL_CONFIG_PARSER_ERROR_PARSE,
"Unknown item '%s' - perhaps you forgot to load the module?", itemname->str);
return FALSE;
} else {
liValue **optlist = g_slice_alloc0(sizeof(liValue*) * si->option_count);
guint i;
/* find options and assign them by id */
hash = li_value_get_single_argument(hash);
if (LI_VALUE_LIST != li_value_type(hash)) {
ERROR(srv, "invalid type '%s' of parameter list", li_value_type_string(hash));
return FALSE;
}
LI_VALUE_FOREACH(entry, hash)
liValue *entryKey = li_value_list_at(entry, 0);
liValue *entryValue = li_value_list_at(entry, 1);
GString *entryKeyStr;
if (LI_VALUE_STRING != li_value_type(entryKey)) {
ERROR(srv, "invalid key of type %s", li_value_type_string(entryKey));
return FALSE;
}
entryKeyStr = entryKey->data.string; /* keys are either NONE or STRING */
for (i = 0; i < si->option_count; i++) {
if (0 == g_strcmp0(si->p_item->options[i].name, entryKeyStr->str)) break;
}
if (i == si->option_count) {
ERROR(srv, "Unknown option '%s' in item '%s'", entryKeyStr->str, itemname->str);
return FALSE;
} else {
optlist[i] = entryValue;
}
LI_VALUE_END_FOREACH()
/* validate options */
for (i = 0; i < si->option_count; i++) {
const liPluginItemOption *pi = &si->p_item->options[i];
if (0 != (pi->flags & LI_PLUGIN_ITEM_OPTION_MANDATORY)) {
if (!optlist[i]) {
ERROR(srv, "Missing mandatory option '%s' in item '%s'", pi->name, itemname->str);
return FALSE;
}
}
if (pi->type != LI_VALUE_NONE && optlist[i] && optlist[i]->type != pi->type) {
/* TODO: convert from string if possible */
ERROR(srv, "Invalid value type of option '%s' in item '%s', got '%s' but expected '%s'",
pi->name, itemname->str, li_value_type_string(optlist[i]), li_valuetype_string(pi->type));
return FALSE;
}
}
g_assert(si->p_item->handle_parse_item);
si->p_item->handle_parse_item(srv, si->p, optlist);
g_slice_free1(sizeof(liValue*) * si->option_count, optlist);
return si->p_item->handle_parse_item(srv, si->p, parameters, err);
}
return TRUE;
}
static gboolean plugins_activate_module(liServer *srv, server_module *sm) {

645
src/angel/angel_plugin_core.c

@ -1,5 +1,6 @@
#include <lighttpd/angel_plugin_core.h>
#include <lighttpd/angel_config_parser.h>
#include <lighttpd/ip_parsers.h>
#include <fnmatch.h>
@ -28,186 +29,271 @@ struct listen_ref_resource {
listen_socket *sock;
};
static void core_instance_parse(liServer *srv, liPlugin *p, liValue **options) {
GPtrArray *cmd, *env;
gchar **cmdarr, **envarr;
liPluginCoreConfig *config = (liPluginCoreConfig*) p->data;
uid_t uid = -1;
gid_t gid = -1;
GString *user = NULL;
gint64 rlim_core = -1, rlim_nofile = -1;
if (config->load_instconf) {
ERROR(srv, "%s", "Already configure the instance");
config->load_failed = FALSE;
return;
static liValue* core_parse_check_parameter_string(liValue *value, const char* item, GError **err) {
value = li_value_get_single_argument(value);
if (LI_VALUE_STRING != li_value_type(value)) {
g_set_error(err, LI_ANGEL_CONFIG_PARSER_ERROR, LI_ANGEL_CONFIG_PARSER_ERROR_PARSE,
"%s: expecting a string as parameter", item);
return NULL;
}
/* set user and group */
if (options[0]) {
struct passwd *pwd;
user = options[0]->data.string;
if (NULL == (pwd = getpwnam(user->str))) {
ERROR(srv, "can't find username '%s'", user->str);
config->load_failed = FALSE;
return;
}
return value;
}
/* destroys value, extracting the contained string into *target */
static gboolean core_parse_store_string(liValue *value, const char* item, GString** target, GError **err) {
if (NULL != *target) {
g_set_error(err, LI_ANGEL_CONFIG_PARSER_ERROR, LI_ANGEL_CONFIG_PARSER_ERROR_PARSE,
"%s: already specified, can only be used once", item);
return FALSE;
}
uid = pwd->pw_uid;
gid = pwd->pw_gid;
if (NULL == (value = core_parse_check_parameter_string(value, item, err))) return FALSE;
*target = li_value_extract_string(value);
return TRUE;
}
/* destroys value, adding the contained strings (char*) to target */
static gboolean core_parse_store_string_list(liValue *value, const char* item, GPtrArray* list, GError **err) {
value = li_value_get_single_argument(value);
if (LI_VALUE_STRING == li_value_type(value)) {
li_value_wrap_in_list(value);
}
else if (LI_VALUE_LIST != li_value_type(value)) goto parameter_type_error;
if (options[1]) {
struct group *grp;
GString *group = options[1]->data.string;
if (NULL == (grp = getgrnam(group->str))) {
ERROR(srv, "can't find groupname '%s'", group->str);
config->load_failed = FALSE;
return;
}
LI_VALUE_FOREACH(entry, value)
if (LI_VALUE_STRING != li_value_type(entry)) goto parameter_type_error;
g_ptr_array_add(list, g_string_free(li_value_extract_string(entry), FALSE));
LI_VALUE_END_FOREACH()
gid = grp->gr_gid;
return TRUE;
parameter_type_error:
g_set_error(err, LI_ANGEL_CONFIG_PARSER_ERROR, LI_ANGEL_CONFIG_PARSER_ERROR_PARSE,
"%s: expecting string list as parameter", item);
return FALSE;
}
/* extract the contained integer into *target */
static gboolean core_parse_store_integer(liValue *value, const char* item, gint64* target, GError **err) {
value = li_value_get_single_argument(value);
if (LI_VALUE_NUMBER != li_value_type(value)) {
g_set_error(err, LI_ANGEL_CONFIG_PARSER_ERROR, LI_ANGEL_CONFIG_PARSER_ERROR_PARSE,
"%s: expecting a number as parameter", item);
return FALSE;
}
if (0 == uid) {
ERROR(srv, "%s", "I will not set uid to 0");
config->load_failed = FALSE;
return;
*target = value->data.number;
return TRUE;
}
static gboolean core_parse_user(liServer *srv, liPlugin *p, liValue *value, GError **err) {
liPluginCoreConfig *pc = p->data;
struct passwd *pwd;
GString *user;
UNUSED(srv);
if (!core_parse_store_string(value, "user", &pc->parsing.user, err)) return FALSE;
user = pc->parsing.user;
if (NULL == (pwd = getpwnam(user->str))) {
g_set_error(err, LI_ANGEL_CONFIG_PARSER_ERROR, LI_ANGEL_CONFIG_PARSER_ERROR_PARSE,
"user: couldn't find user '%s' ", user->str);
return FALSE;
}
if (0 == gid) {
ERROR(srv, "%s", "I will not set gid to 0");
config->load_failed = FALSE;
return;
if (0 == pwd->pw_uid) {
g_set_error(err, LI_ANGEL_CONFIG_PARSER_ERROR, LI_ANGEL_CONFIG_PARSER_ERROR_PARSE,
"user: will not changed to uid 0");
return FALSE;
}
/* check types in lists */
if (options[6]) {
GPtrArray *wrapper_lst = options[6]->data.list;
guint i;
for (i = 0; i < wrapper_lst->len; i++) {
liValue *wi = g_ptr_array_index(wrapper_lst, i);
if (wi->type != LI_VALUE_STRING) {
ERROR(srv, "Entry %i in wrapper list is not a string", i);
config->load_failed = FALSE;
return;
}
}
if (0 == pwd->pw_gid) {
g_set_error(err, LI_ANGEL_CONFIG_PARSER_ERROR, LI_ANGEL_CONFIG_PARSER_ERROR_PARSE,
"user: will not changed to gid 0");
return FALSE;
}
if (options[7]) { /* env */
GPtrArray *env_lst = options[7]->data.list;
guint i;
for (i = 0; i < env_lst->len; i++) {
liValue *ei = g_ptr_array_index(env_lst, i);
if (ei->type != LI_VALUE_STRING) {
ERROR(srv, "Entry %i in env list is not a string", i);
config->load_failed = FALSE;
return;
}
}
pc->parsing.user_uid = pwd->pw_uid;
pc->parsing.user_gid = pwd->pw_gid;
return TRUE;
}
static gboolean core_parse_group(liServer *srv, liPlugin *p, liValue *value, GError **err) {
liPluginCoreConfig *pc = p->data;
struct group *grp;
GString *group;
UNUSED(srv);
if (!core_parse_store_string(value, "group", &pc->parsing.group, err)) return FALSE;
group = pc->parsing.group;
if (NULL == (grp = getgrnam(group->str))) {
g_set_error(err, LI_ANGEL_CONFIG_PARSER_ERROR, LI_ANGEL_CONFIG_PARSER_ERROR_PARSE,
"group: couldn't find group '%s' ", group->str);
return FALSE;
}
if (options[8]) { /* copy-env */
GPtrArray *env_lst = options[8]->data.list;
guint i;
for (i = 0; i < env_lst->len; i++) {
liValue *ei = g_ptr_array_index(env_lst, i);
if (ei->type != LI_VALUE_STRING) {
ERROR(srv, "Entry %i in copy-env list is not a string", i);
config->load_failed = FALSE;
return;
}
}
if (0 == grp->gr_gid) {
g_set_error(err, LI_ANGEL_CONFIG_PARSER_ERROR, LI_ANGEL_CONFIG_PARSER_ERROR_PARSE,
"group: will not changed to gid 0");
return FALSE;
}
env = g_ptr_array_new();
pc->parsing.group_gid = grp->gr_gid;
if (options[7]) { /* env */
GPtrArray *env_lst = options[7]->data.list;
guint i;
for (i = 0; i < env_lst->len; i++) {
liValue *ei = g_ptr_array_index(env_lst, i);
g_ptr_array_add(env, g_strndup(GSTR_LEN(ei->data.string)));
}
return TRUE;
}
static gboolean core_parse_binary(liServer *srv, liPlugin *p, liValue *value, GError **err) {
liPluginCoreConfig *pc = p->data;
UNUSED(srv);
return core_parse_store_string(value, "binary", &pc->parsing.binary, err);
}
static gboolean core_parse_config(liServer *srv, liPlugin *p, liValue *value, GError **err) {
liPluginCoreConfig *pc = p->data;
UNUSED(srv);
if (NULL != pc->parsing.luaconfig) {
g_set_error(err, LI_ANGEL_CONFIG_PARSER_ERROR, LI_ANGEL_CONFIG_PARSER_ERROR_PARSE,
"config: already specified luaconfig");
return FALSE;
}
if (options[8]) { /* copy-env */
GPtrArray *env_lst = options[8]->data.list;
guint i;
for (i = 0; i < env_lst->len; i++) {
liValue *ei = g_ptr_array_index(env_lst, i);
const char *val = getenv(ei->data.string->str);
size_t vallen, keylen = ei->data.string->len;
gchar *entry;
if (!val) continue;
vallen = strlen(val);
entry = g_malloc(keylen + 1 /* "=" */ + vallen + 1 /* \0 */);
memcpy(entry, ei->data.string->str, keylen);
entry[keylen] = '=';
memcpy(entry + keylen+1, val, vallen);
entry[keylen + vallen + 1] = '\0';
g_ptr_array_add(env, entry);
}
return core_parse_store_string(value, "config", &pc->parsing.config, err);
}
static gboolean core_parse_luaconfig(liServer *srv, liPlugin *p, liValue *value, GError **err) {
liPluginCoreConfig *pc = p->data;
UNUSED(srv);
if (NULL != pc->parsing.config) {
g_set_error(err, LI_ANGEL_CONFIG_PARSER_ERROR, LI_ANGEL_CONFIG_PARSER_ERROR_PARSE,
"luaconfig: already specified config");
return FALSE;
}
return core_parse_store_string(value, "luaconfig", &pc->parsing.luaconfig, err);
}
static gboolean core_parse_modules_path(liServer *srv, liPlugin *p, liValue *value, GError **err) {
liPluginCoreConfig *pc = p->data;
UNUSED(srv);
cmd = g_ptr_array_new();
return core_parse_store_string(value, "modules_path", &pc->parsing.modules_path, err);
}
if (options[6]) {
GPtrArray *wrapper_lst = options[6]->data.list;
guint i;
for (i = 0; i < wrapper_lst->len; i++) {
liValue *wi = g_ptr_array_index(wrapper_lst, i);
g_ptr_array_add(cmd, g_strndup(GSTR_LEN(wi->data.string)));
static void add_env(GPtrArray *env, const char *key, size_t keylen, const char *value, size_t valuelen) {
gchar* entry = g_malloc(keylen + 1 /* "=" */ + valuelen + 1 /* \0 */);
memcpy(entry, key, keylen);
entry[keylen] = '=';
memcpy(entry + keylen + 1, value, valuelen);
entry[keylen + valuelen + 1] = '\0';
g_ptr_array_add(env, entry);
}
static gboolean core_parse_env(liServer *srv, liPlugin *p, liValue *value, GError **err) {
liPluginCoreConfig *pc = p->data;
UNUSED(srv);
value = li_value_get_single_argument(value);
if (LI_VALUE_LIST != li_value_type(value)) goto parameter_type_error;
if (li_value_list_has_len(value, 2) && LI_VALUE_STRING == li_value_list_type_at(value, 0) && LI_VALUE_STRING == li_value_list_type_at(value, 1)) {
if (NULL == strchr(li_value_list_at(value, 0)->data.string->str, '=')) {
/* no '=' in first string: single key => value pair; otherwise a list with two entries ['foo=x', 'bar=y'] */
li_value_wrap_in_list(value);
}
}
if (options[2]) {
g_ptr_array_add(cmd, g_strndup(GSTR_LEN(options[2]->data.string)));
} else {
g_ptr_array_add(cmd, g_strndup(CONST_STR_LEN(DEFAULT_LIBEXECDIR "/lighttpd2-worker")));
LI_VALUE_FOREACH(entry, value)
if (LI_VALUE_STRING == li_value_type(entry)) {
g_ptr_array_add(pc->parsing.env, g_string_free(li_value_extract_string(entry), FALSE));
} else {
liValue *key = li_value_list_at(entry, 0);
liValue *val = li_value_list_at(entry, 1);
if (!li_value_list_has_len(entry, 2) || LI_VALUE_STRING != li_value_type(key) || LI_VALUE_STRING != li_value_type(val)) goto parameter_type_error;
add_env(pc->parsing.env, GSTR_LEN(key->data.string), GSTR_LEN(val->data.string));
}
LI_VALUE_END_FOREACH()
return TRUE;
parameter_type_error:
g_set_error(err, LI_ANGEL_CONFIG_PARSER_ERROR, LI_ANGEL_CONFIG_PARSER_ERROR_PARSE,
"env: expecting key-value/string list as parameter");
return FALSE;
}
static gboolean core_parse_copy_env(liServer *srv, liPlugin *p, liValue *value, GError **err) {
liPluginCoreConfig *pc = p->data;
UNUSED(srv);
value = li_value_get_single_argument(value);
if (LI_VALUE_STRING == li_value_type(value)) {
li_value_wrap_in_list(value);
}
else if (LI_VALUE_LIST != li_value_type(value)) goto parameter_type_error;
g_ptr_array_add(cmd, g_strndup(CONST_STR_LEN("--angel")));
g_ptr_array_add(cmd, g_strndup(CONST_STR_LEN("-c")));
if (options[3]) {
g_ptr_array_add(cmd, g_strndup(GSTR_LEN(options[3]->data.string)));
} else if (options[4]) {
g_ptr_array_add(cmd, g_strndup(GSTR_LEN(options[4]->data.string)));
g_ptr_array_add(cmd, g_strndup(CONST_STR_LEN("-l")));
} else {
g_ptr_array_add(cmd, g_strndup(CONST_STR_LEN("/etc/lighttpd2/lighttpd.conf")));
LI_VALUE_FOREACH(entry, value)
const char *val;
size_t vallen;
if (LI_VALUE_STRING != li_value_type(entry)) goto parameter_type_error;
val = getenv(entry->data.string->str);
if (NULL == val) continue;
vallen = strlen(val);
add_env(pc->parsing.env, GSTR_LEN(entry->data.string), val, vallen);
LI_VALUE_END_FOREACH()
return TRUE;
parameter_type_error:
g_set_error(err, LI_ANGEL_CONFIG_PARSER_ERROR, LI_ANGEL_CONFIG_PARSER_ERROR_PARSE,
"copy_env: expecting string list as parameter");
return FALSE;
}
static gboolean core_parse_wrapper(liServer *srv, liPlugin *p, liValue *value, GError **err) {
liPluginCoreConfig *pc = p->data;
UNUSED(srv);
return core_parse_store_string_list(value, "wrapper", pc->parsing.wrapper, err);
}
static gboolean core_parse_max_core_file_size(liServer *srv, liPlugin *p, liValue *value, GError **err) {
liPluginCoreConfig *pc = p->data;
UNUSED(srv);
if (-1 != pc->parsing.rlim_core) {
g_set_error(err, LI_ANGEL_CONFIG_PARSER_ERROR, LI_ANGEL_CONFIG_PARSER_ERROR_PARSE,
"max_core_file_size: already specified");
return FALSE;
}
if (options[5]) {
g_ptr_array_add(cmd, g_strndup(CONST_STR_LEN("-m")));
g_ptr_array_add(cmd, g_strndup(GSTR_LEN(options[5]->data.string)));
return core_parse_store_integer(value, "max_core_file_size", &pc->parsing.rlim_core, err);
}
static gboolean core_parse_max_open_files(liServer *srv, liPlugin *p, liValue *value, GError **err) {
liPluginCoreConfig *pc = p->data;
UNUSED(srv);
if (-1 != pc->parsing.rlim_nofile) {
g_set_error(err, LI_ANGEL_CONFIG_PARSER_ERROR, LI_ANGEL_CONFIG_PARSER_ERROR_PARSE,
"max_open_files: already specified");
return FALSE;
}
if (options[9]) rlim_core = options[9]->data.number;
if (options[10]) rlim_nofile = options[10]->data.number;
return core_parse_store_integer(value, "max_open_files", &pc->parsing.rlim_nofile, err);
}
g_ptr_array_add(cmd, NULL);
g_ptr_array_add(env, NULL);
cmdarr = (gchar**) g_ptr_array_free(cmd, FALSE);
envarr = (gchar**) g_ptr_array_free(env, FALSE);
config->load_instconf = li_instance_conf_new(cmdarr, envarr, user, uid, gid, rlim_core, rlim_nofile);
}
static const liPluginItemOption core_instance_options[] = {
/* 0 */ { "user", LI_VALUE_STRING, 0 },
/* 1 */ { "group", LI_VALUE_STRING, 0 },
/* 2 */ { "binary", LI_VALUE_STRING, 0 },
/* 3 */ { "config", LI_VALUE_STRING, 0 },
/* 4 */ { "luaconfig", LI_VALUE_STRING, 0 },
/* 5 */ { "modules", LI_VALUE_STRING, 0 },
/* 6 */ { "wrapper", LI_VALUE_LIST, 0 },
/* 7 */ { "env", LI_VALUE_LIST, 0 },
/* 8 */ { "copy-env", LI_VALUE_LIST, 0 },
/* 9 */ { "max-core-file-size", LI_VALUE_NUMBER, 0 },
/* 10 */ { "max-open-files", LI_VALUE_NUMBER, 0 },
{ NULL, 0, 0 }
};
static void core_listen_mask_free(liPluginCoreListenMask *mask) {
if (NULL == mask) return;
switch (mask->type) {
case LI_PLUGIN_CORE_LISTEN_MASK_IPV4:
case LI_PLUGIN_CORE_LISTEN_MASK_IPV6:
@ -219,63 +305,169 @@ static void core_listen_mask_free(liPluginCoreListenMask *mask) {
g_slice_free(liPluginCoreListenMask, mask);
}
static void core_listen_parse(liServer *srv, liPlugin *p, liValue **options) {
liPluginCoreConfig *config = (liPluginCoreConfig*) p->data;
gboolean have_type = FALSE;
static gboolean core_parse_allow_listen_ip(liServer *srv, liPlugin *p, liValue *value, GError **err) {
liPluginCoreConfig *pc = p->data;
liPluginCoreListenMask *mask;
UNUSED(srv);
liPluginCoreListenMask *mask = g_slice_new0(liPluginCoreListenMask);
if (NULL == (value = core_parse_check_parameter_string(value, "allow_listen_ip", err))) return FALSE;
if (options[0]) { /* ip */
if (have_type) goto only_one_type;
have_type = TRUE;
if (li_parse_ipv4(options[0]->data.string->str, &mask->value.ipv4.addr, &mask->value.ipv4.networkmask, &mask->value.ipv4.port)) {
mask->type = LI_PLUGIN_CORE_LISTEN_MASK_IPV4;
} else if (li_parse_ipv6(options[0]->data.string->str, mask->value.ipv6.addr, &mask->value.ipv6.network, &mask->value.ipv6.port)) {
mask->type = LI_PLUGIN_CORE_LISTEN_MASK_IPV6;
} else {
ERROR(srv, "couldn't parse ip/network:port in listen mask '%s'", options[0]->data.string->str);
config->load_failed = FALSE;
g_slice_free(liPluginCoreListenMask, mask);
return;
mask = g_slice_new0(liPluginCoreListenMask);
if (li_parse_ipv4(value->data.string->str, &mask->value.ipv4.addr, &mask->value.ipv4.networkmask, &mask->value.ipv4.port)) {
mask->type = LI_PLUGIN_CORE_LISTEN_MASK_IPV4;
} else if (li_parse_ipv6(value->data.string->str, mask->value.ipv6.addr, &mask->value.ipv6.network, &mask->value.ipv6.port)) {
mask->type = LI_PLUGIN_CORE_LISTEN_MASK_IPV6;
} else {
ERROR(srv, "couldn't parse ip/network:port in allow_listen_ip mask '%s'", value->data.string->str);
g_slice_free(liPluginCoreListenMask, mask);
return FALSE;
}
g_ptr_array_add(pc->parsing.listen_masks, mask);
return TRUE;
}
static gboolean core_parse_allow_listen_unix(liServer *srv, liPlugin *p, liValue *value, GError **err) {
liPluginCoreConfig *pc = p->data;
liPluginCoreListenMask *mask;
UNUSED(srv);
if (NULL == (value = core_parse_check_parameter_string(value, "allow_listen_unix", err))) return FALSE;
mask = g_slice_new0(liPluginCoreListenMask);
mask->type = LI_PLUGIN_CORE_LISTEN_MASK_UNIX;
mask->value.unix_socket.path = li_value_extract_string(value);
g_ptr_array_add(pc->parsing.listen_masks, mask);
return TRUE;
}
static const liPluginItem core_items[] = {
{ "user", core_parse_user },
{ "group", core_parse_group },
{ "binary", core_parse_binary },
{ "config", core_parse_config },
{ "luaconfig", core_parse_luaconfig },
{ "modules_path", core_parse_modules_path },
{ "wrapper", core_parse_wrapper },
{ "env", core_parse_env },
{ "copy_env", core_parse_copy_env },
{ "max_core_file_size", core_parse_max_core_file_size },
{ "max_open_files", core_parse_max_open_files },
{ "allow_listen_ip", core_parse_allow_listen_ip },
{ "allow_listen_unix", core_parse_allow_listen_unix },
{ NULL, NULL }
};
#define INIT_STR(N) /* GString, init to NULL */ \
if (NULL != pc->parsing.N) { \
g_string_free(pc->parsing.N, TRUE); \
pc->parsing.N = NULL; \
}
#define INIT_STR_LIST(N) /* char* ptr array, init to empty array */ \
if (NULL != pc->parsing.N) { \
GPtrArray *a_ ## N = pc->parsing.N; \
guint i_ ## N; \
for (i_ ## N = 0; i_ ## N < a_ ## N->len; ++i_ ## N) { \
g_free(g_ptr_array_index(a_ ## N, i_ ## N)); \
} \
g_ptr_array_set_size(a_ ## N, 0); \
} else { \
pc->parsing.N = g_ptr_array_new(); \
}
static void core_parse_init(liServer *srv, liPlugin *p) {
liPluginCoreConfig *pc = p->data;
UNUSED(srv);
INIT_STR_LIST(env);
INIT_STR(user);
pc->parsing.user_uid = (uid_t) -1;
pc->parsing.user_gid = (gid_t) -1;
INIT_STR(group);
pc->parsing.group_gid = (gid_t) -1;
INIT_STR(binary);
INIT_STR(config);
INIT_STR(luaconfig);
INIT_STR(modules_path);
INIT_STR_LIST(wrapper);
pc->parsing.rlim_core = pc->parsing.rlim_nofile = -1;
if (NULL != pc->parsing.instconf) {
li_instance_conf_release(pc->parsing.instconf);
pc->parsing.instconf = NULL;
}
if (NULL != pc->parsing.listen_masks) {
GPtrArray *a = pc->parsing.listen_masks;
guint i;
for (i = 0; i < a->len; ++i) {
core_listen_mask_free(g_ptr_array_index(a, i));
}
g_ptr_array_set_size(a, 0);
} else {
pc->parsing.listen_masks = g_ptr_array_new();
}
}
if (options[1]) { /* unix */
if (have_type) goto only_one_type;
have_type = TRUE;
mask->type = LI_PLUGIN_CORE_LISTEN_MASK_UNIX;
mask->value.unix_socket.path = g_string_new_len(GSTR_LEN(options[2]->data.string));
static gboolean core_check(liServer *srv, liPlugin *p, GError **err) {
GPtrArray *cmd;
GString *user;
gchar **cmdarr, **envarr;
liPluginCoreConfig *pc = p->data;
gid_t gid = ((gid_t)-1 != pc->parsing.group_gid) ? pc->parsing.group_gid : pc->parsing.user_gid;
UNUSED(srv);
UNUSED(err);
cmd = pc->parsing.wrapper;
pc->parsing.wrapper = NULL;
if (NULL != pc->parsing.binary) {
g_ptr_array_add(cmd, g_string_free(pc->parsing.binary, FALSE));
pc->parsing.binary = NULL;
} else {
g_ptr_array_add(cmd, g_strndup(CONST_STR_LEN(DEFAULT_LIBEXECDIR "/lighttpd2-worker")));
}
if (!have_type) {
ERROR(srv, "%s", "no options found in listen mask");
config->load_failed = FALSE;
g_slice_free(liPluginCoreListenMask, mask);
return;
g_ptr_array_add(cmd, g_strndup(CONST_STR_LEN("--angel")));
g_ptr_array_add(cmd, g_strndup(CONST_STR_LEN("-c")));
if (NULL != pc->parsing.config) {
g_ptr_array_add(cmd, g_string_free(pc->parsing.config, FALSE));
pc->parsing.config = NULL;
} else if (NULL != pc->parsing.luaconfig) {
g_ptr_array_add(cmd, g_string_free(pc->parsing.luaconfig, FALSE));
pc->parsing.luaconfig = NULL;
g_ptr_array_add(cmd, g_strndup(CONST_STR_LEN("-l")));
} else {
g_ptr_array_add(cmd, g_strndup(CONST_STR_LEN("/etc/lighttpd2/lighttpd.conf")));
}
g_ptr_array_add(config->load_listen_masks, mask);
return;
if (NULL != pc->parsing.modules_path) {
g_ptr_array_add(cmd, g_strndup(CONST_STR_LEN("-m")));
g_ptr_array_add(cmd, g_string_free(pc->parsing.modules_path, FALSE));
pc->parsing.modules_path = NULL;
}
only_one_type:
ERROR(srv, "%s", "you can only use one of 'ip' and 'unix' in listen masks");
config->load_failed = FALSE;
g_slice_free(liPluginCoreListenMask, mask);
return;
}
g_ptr_array_add(cmd, NULL);
g_ptr_array_add(pc->parsing.env, NULL);
cmdarr = (gchar**) g_ptr_array_free(cmd, FALSE);
envarr = (gchar**) g_ptr_array_free(pc->parsing.env, FALSE);
pc->parsing.env = NULL;
static const liPluginItemOption core_listen_options[] = {
/* 0 */ { "ip", LI_VALUE_STRING, 0 },
/* 1 */ { "unix", LI_VALUE_STRING, 0 },
{ NULL, 0, 0 }
};
user = pc->parsing.user;
pc->parsing.user = NULL;
pc->parsing.instconf = li_instance_conf_new(cmdarr, envarr, user, pc->parsing.user_uid, gid,
pc->parsing.rlim_core, pc->parsing.rlim_nofile);
return TRUE;
}
static const liPluginItem core_items[] = {
{ "instance", core_instance_parse, core_instance_options },
{ "allow_listen", core_listen_parse, core_listen_options },
{ NULL, NULL, NULL }
};
static listen_socket* listen_new_socket(liSocketAddress *addr, int fd) {
listen_socket *sock = g_slice_new0(listen_socket);
@ -343,13 +535,7 @@ static gboolean listen_check_acl(liServer *srv, liPluginCoreConfig *config, liSo
if (!li_ipv4_in_ipv4_net(ipv4->sin_addr.s_addr, mask->value.ipv4.addr, mask->value.ipv4.networkmask)) continue;
if ((mask->value.ipv4.port != port) && (mask->value.ipv4.port != 0 || (port != 80 && port != 443))) continue;
return TRUE;
/* strict matches only */
#if 0
case LI_PLUGIN_CORE_LISTEN_MASK_IPV6:
if (!li_ipv4_in_ipv6_net(ipv4->sin_addr.s_addr, mask->value.ipv6.addr, mask->value.ipv6.network)) continue;
if ((mask->value.ipv6.port != port) && (mask->value.ipv6.port != 0 || (port != 80 && port != 443))) continue;
return TRUE;
#endif
/* strict matches only, no ipv4 in (ipv4-mapped) ipv6 */
default:
continue;
}
@ -368,13 +554,7 @@ static gboolean listen_check_acl(liServer *srv, liPluginCoreConfig *config, liSo
for (i = 0; i < config->listen_masks->len; i++) {
mask = g_ptr_array_index(config->listen_masks, i);
switch (mask->type) {
/* strict matches only */
#if 0
case LI_PLUGIN_CORE_LISTEN_MASK_IPV4:
if (!li_ipv6_in_ipv4_net(ipv6->sin6_addr.s6_addr, mask->value.ipv4.addr, mask->value.ipv4.networkmask)) continue;
if ((mask->value.ipv4.port != port) && (mask->value.ipv4.port != 0 || (port != 80 && port != 443))) continue;
return TRUE;
#endif
/* strict matches only, no (ipv4-mapped) ipv6 in ipv4 */
case LI_PLUGIN_CORE_LISTEN_MASK_IPV6:
if (!li_ipv6_in_ipv6_net(ipv6->sin6_addr.s6_addr, mask->value.ipv6.addr, mask->value.ipv6.network)) continue;
if ((mask->value.ipv6.port != port) && (mask->value.ipv6.port != 0 || (port != 80 && port != 443))) continue;
@ -676,14 +856,19 @@ static void core_log_open_file(liServer *srv, liPlugin *p, liInstance *i, gint32
}
}
static void core_clean(liServer *srv, liPlugin *p);
static void core_free(liServer *srv, liPlugin *p) {
liPluginCoreConfig *config = (liPluginCoreConfig*) p->data;
guint i;
li_event_clear(&config->sig_hup);
core_clean(srv, p);
core_parse_init(srv, p);
g_ptr_array_free(config->parsing.env, TRUE);
config->parsing.env = NULL;
g_ptr_array_free(config->parsing.wrapper, TRUE);
config->parsing.wrapper = NULL;
g_ptr_array_free(config->parsing.listen_masks, TRUE);
config->parsing.listen_masks = NULL;
if (config->instconf) {
li_instance_conf_release(config->instconf);
@ -700,49 +885,23 @@ static void core_free(liServer *srv, liPlugin *p) {
core_listen_mask_free(g_ptr_array_index(config->listen_masks, i));
}
g_ptr_array_free(config->listen_masks, TRUE);
g_ptr_array_free(config->load_listen_masks, TRUE);
g_hash_table_destroy(config->listen_sockets);
config->listen_masks = NULL;
config->load_listen_masks = NULL;
g_slice_free(liPluginCoreConfig, config);
}
static void core_clean(liServer *srv, liPlugin *p) {
liPluginCoreConfig *config = (liPluginCoreConfig*) p->data;
guint i;
UNUSED(srv);
if (config->load_instconf) {
li_instance_conf_release(config->load_instconf);
config->load_instconf = NULL;
}
for (i = 0; i < config->load_listen_masks->len; i++) {
core_listen_mask_free(g_ptr_array_index(config->load_listen_masks, i));
}
g_ptr_array_set_size(config->load_listen_masks, 0);
config->load_failed = FALSE;
}
static gboolean core_check(liServer *srv, liPlugin *p) {
liPluginCoreConfig *config = (liPluginCoreConfig*) p->data;