diff options
Diffstat (limited to 'src/inetd.c')
-rw-r--r-- | src/inetd.c | 320 |
1 files changed, 320 insertions, 0 deletions
diff --git a/src/inetd.c b/src/inetd.c new file mode 100644 index 0000000..ac40a5a --- /dev/null +++ b/src/inetd.c @@ -0,0 +1,320 @@ +/* This file is part of GNU Pies. + Copyright (C) 2009 Sergey Poznyakoff + + GNU Pies is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GNU Pies is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Pies. If not, see <http://www.gnu.org/licenses/>. */ + +#include "pies.h" +#include <sys/types.h> +#include <dirent.h> + +#define IFLD_SERVICE 0 /* service name */ +#define IFLD_SOCKET 1 /* socket type */ +#define IFLD_PROTOCOL 2 /* protocol */ +#define IFLD_WAIT 3 /* wait/nowait */ +#define IFLD_USER 4 /* user */ +#define IFLD_SERVER_PATH 5 /* server program path */ +#define IFLD_SERVER_ARGS 6 /* server program arguments */ + +#define IFLD_MIN_COUNT 6 /* Minimum number of fields in entry */ + +/* FIXME: Duplicated in grex-lex.l */ +static void +listel_dispose(const void *el) +{ + free((void*)el); +} + +static int +inetd_conf_file (const char *file) +{ + FILE *fp; + size_t size = 0; + char *buf = NULL; + size_t line_no = 0; + struct wordsplit ws; + char *dfl_address = NULL; + + fp = fopen (file, "r"); + if (!fp) + { + logmsg (LOG_ERR, + _("cannot open configuration file %s: %s"), + file, strerror (errno)); + return 1; + } + + while (getline (&buf, &size, fp) >= 0) + { + char *p; + struct component *comp; + int socket_type; + struct pies_url *url; + size_t max_instances = 0; + int flags = 0; + char *str; + char *user = NULL; + char *group = NULL; + char *address; + char *service; + size_t len; + + if (line_no) + wordsplit_free (&ws); + + line_no++; + for (p = buf; *p && c_isblank (*p); p++) + ; + + if (!p || *p == '\n' || *p == '#') + continue; + + if (wordsplit (p, &ws, + WRDSF_NOVAR | WRDSF_NOCMD | WRDSF_WS | + WRDSF_SQUEEZE_DELIMS)) + { + logmsg (LOG_ERR, "wordsplit: %s", strerror (errno)); + continue; + } + + if (ws.ws_wordc == 1) + { + size_t len = strlen (ws.ws_wordv[IFLD_SERVICE]); + if (len > 0 && ws.ws_wordv[IFLD_SERVICE][len-1] == ':') + { + free (dfl_address); + + if (len == 2 && ws.ws_wordv[IFLD_SERVICE][0] == '*') + dfl_address = NULL; + else + { + dfl_address = xmalloc (len); + memcpy (dfl_address, ws.ws_wordv[IFLD_SERVICE], len-1); + dfl_address[len-1] = 0; + } + continue; + } + } + + if (ws.ws_wordc < IFLD_MIN_COUNT) + { + logmsg (LOG_ERR, "%s:%lu: too few fields", file, line_no); + continue; + } + + /* Parse service */ + str = strchr (ws.ws_wordv[IFLD_SERVICE], ':'); + if (str) + { + *str++ = 0; + address = ws.ws_wordv[IFLD_SERVICE]; + service = str; + } + else + { + address = dfl_address; + service = ws.ws_wordv[IFLD_SERVICE]; + } + + /* Parse socket type */ + if (str_to_socket_type (ws.ws_wordv[IFLD_SOCKET], &socket_type)) + { + logmsg (LOG_ERR, "%s:%lu: %s", + file, line_no, _("bad socket type")); + continue; + } + + /* Create URL from protocol and service fields. */ + str = xasprintf ("inet+%s://%s:%s", + ws.ws_wordv[IFLD_PROTOCOL], + address ? address : "0.0.0.0", + service); + if (pies_url_create (&url, str)) + { + /* FIXME: Better error message */ + logmsg (LOG_ERR, "%s:%lu: %s", + file, line_no, _("invalid socket address")); + continue; + } + + free (str); + + /* Parse wait/nowait field */ + str = strchr (ws.ws_wordv[IFLD_WAIT], '.'); + if (str) + { + size_t n; + + *str++ = 0; + n = strtoul(str, &p, 10); + if (*p) + logmsg (LOG_WARNING, "%s:%lu: invalid number (near %s)", + file, line_no, p); + else + max_instances = n; + } + + if (strcmp (ws.ws_wordv[IFLD_WAIT], "wait") == 0) + flags |= CF_WAIT; + else if (strcmp (ws.ws_wordv[IFLD_WAIT], "nowait")) + { + logmsg (LOG_ERR, "%s:%lu: %s", + file, line_no, _("invalid wait field")); + pies_url_destroy(&url); + continue; + } + + /* Parse user/group */ + len = strcspn (ws.ws_wordv[IFLD_USER], ":."); + if (ws.ws_wordv[IFLD_USER][len]) + { + ws.ws_wordv[IFLD_USER][len] = 0; + group = ws.ws_wordv[IFLD_USER] + len + 1; + } + user = ws.ws_wordv[IFLD_USER]; + + /* Create the component */ + if (address) + { + str = xmalloc (strlen (address) + 1 + strlen (service) + 1); + strcpy (str, address); + strcat (str, ":"); + strcat (str, service); + comp = component_create (str); + free (str); + } + else + comp = component_create (service); + + comp->mode = pies_comp_inetd; + comp->socket_type = socket_type; + comp->socket_url = url; + comp->max_instances = max_instances; + comp->flags = flags; + comp->privs.user = xstrdup (user); /* FIXME: memory leak */ + if (group) + { + comp->privs.groups = + gl_list_create_empty (&gl_linked_list_implementation, + NULL, + NULL, + listel_dispose, + true); + gl_list_add_last (comp->privs.groups, xstrdup (group)); + } + + comp->program = xstrdup (ws.ws_wordv[IFLD_SERVER_PATH]); + + if (ws.ws_wordc > IFLD_MIN_COUNT) + { + size_t i, j; + + comp->argc = ws.ws_wordc - IFLD_MIN_COUNT; + comp->argv = xcalloc (comp->argc + 1, sizeof (comp->argv[0])); + for (i = IFLD_SERVER_ARGS, j = 0; i < ws.ws_wordc; i++, j++) + comp->argv[j] = xstrdup (ws.ws_wordv[i]); + } + else + { + comp->argc = 1; + comp->argv = xcalloc (comp->argc + 1, sizeof (comp->argv[0])); + comp->argv[0] = xstrdup (comp->program); + } + + if (progman_lookup_component (comp->tag) == NULL) + register_prog (comp); + } + + if (line_no) + wordsplit_free (&ws); + free (dfl_address); + free (buf); + fclose (fp); + return 0; +} + +#ifndef S_ISLNK +# define S_ISLNK(m) 0 +#endif +#define NAME_INIT_ALLOC 16 + +static int +inetd_conf_dir (const char *name) +{ + DIR *dir; + struct dirent *ent; + int errs = 0; + char *namebuf; + size_t namebufsize; + size_t namelen; + + dir = opendir (name); + if (!dir) + { + logmsg (LOG_ERR, _("cannot open directory %s: %s"), + name, strerror (errno)); + return 1; + } + + namelen = strlen (name); + namebufsize = namelen; + if (name[namelen-1] != '/') + namebufsize++; + namebufsize += NAME_INIT_ALLOC; + namebuf = xmalloc (namebufsize); + memcpy (namebuf, name, namelen); + if (name[namelen-1] != '/') + namebuf[namelen++] = 0; + + while ((ent = readdir (dir))) + { + struct stat st; + + if (stat (ent->d_name, &st)) + { + logmsg (LOG_ERR, _("cannot stat %s/%s: %s"), + name, ent->d_name, strerror (errno)); + errs |= 1; + } + else if (S_ISREG (st.st_mode) || S_ISLNK (st.st_mode)) + { + size_t len = strlen (ent->d_name); + if (namelen + len >= namebufsize) + { + namebufsize = namelen + len + 1; + namebuf = xrealloc (namebuf, namebufsize); + } + strcpy (namebuf + namelen, ent->d_name); + errs |= inetd_conf_file (namebuf); + } + } + free (namebuf); + closedir (dir); + return errs; +} + +int +inetd_parse_conf (const char *file) +{ + struct stat st; + + if (stat (file, &st)) + { + logmsg (LOG_ERR, _("cannot stat %s: %s"), file, strerror (errno)); + return 1; + } + if (S_ISDIR (st.st_mode)) + return inetd_conf_dir (file); + + return inetd_conf_file (file); +} |