/* 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 . */ #include "pies.h" #include #include #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); }