From 68796f86fe6abae13debf253c0e16b4bac9c1bca Mon Sep 17 00:00:00 2001 From: Sergey Poznyakoff Date: Tue, 24 Nov 2009 00:44:28 +0200 Subject: Handle non-stream sockets. Pies can now replace inetd (- inetd.conf and built-in services). * src/pies.c (component_keywords): New keywords: wait, socket-type. (component_verify): Add more checks. * src/pies.h (CF_WAIT): New define. (struct component): New member: socket_type. (struct pies_url): Rename proto to scheme. New members: proto, proto_s, port_s (register_listener): Remove proto. (register_socket): New proto. (create_socket): Change signature. (disable_socket, enable_socket): New protos. * src/progman.c (close_fds): New function. (open_redirector): Use close_fds. (prog_start): Use close_fds. Update call to create_socket. Disable socket if wait is set. (progman_accept): Support non-stream (and stream+wait) sockets. (component_fixup_depend): Update call to create_socket. Call register_socket. (run_command): Use close_fds. Re-enable socket if wait is set. * src/socket.c (create_socket): Take additional argument: socket_type. (register_socket): New function. (disable_socket, enable_socket): New functions. (pies_pause): Add missing break. * src/url.c (url_parse_host): Accept service name and numberic port number. (url_parse_proto): Rename to url_parse_scheme. All callers updated. (url_parse_scheme): Allow for optional protocol specification in scheme field (after a '+' sign). (pies_url_destroy): Free new fields. --- src/pies.c | 72 ++++++++++++++++++++++++++++++++- src/pies.h | 17 ++++++-- src/progman.c | 126 ++++++++++++++++++++++++++++++++++++++++++---------------- src/socket.c | 32 +++++++++++---- src/url.c | 112 ++++++++++++++++++++++++++++++++++++--------------- 5 files changed, 278 insertions(+), 81 deletions(-) (limited to 'src') diff --git a/src/pies.c b/src/pies.c index dbe4ca0..2bf127c 100644 --- a/src/pies.c +++ b/src/pies.c @@ -712,6 +712,36 @@ _cb_url (enum grecs_callback_command cmd, return 0; } +static int +_cb_socket_type (enum grecs_callback_command cmd, + grecs_locus_t *locus, + void *varptr, grecs_value_t *value, void *cb_data) +{ + int t; + + if (assert_scalar_stmt (locus, cmd) + || assert_grecs_value_type (locus, value, GRECS_TYPE_STRING)) + return 1; + + if (strcmp (value->v.string, "stream") == 0) + t = SOCK_STREAM; + else if (strcmp (value->v.string, "dgram") == 0) + t = SOCK_DGRAM; + else if (strcmp (value->v.string, "rdm") == 0) + t = SOCK_RDM; + else if (strcmp (value->v.string, "seqpacket") == 0) + t = SOCK_SEQPACKET; + else if (strcmp (value->v.string, "raw") == 0) + t = SOCK_RAW; + else + { + grecs_error (locus, 0, _("bad socket type")); + return 0; + } + *(int*)varptr = t; + return 0; +} + static struct tokendef modetab[] = { {"exec", pies_comp_exec}, {"wait", pies_comp_exec}, @@ -791,6 +821,14 @@ _cb_disabled (enum grecs_callback_command cmd, return _cb_bitmask (cmd, locus, varptr, value, CF_DISABLED); } +static int +_cb_wait (enum grecs_callback_command cmd, + grecs_locus_t *locus, + void *varptr, grecs_value_t *value, void *cb_data) +{ + return _cb_bitmask (cmd, locus, varptr, value, CF_WAIT); +} + struct grecs_keyword component_keywords[] = { {"mode", /* TRANSLATORS: The words between '{' and '}' are keywords, do not @@ -846,6 +884,13 @@ struct grecs_keyword component_keywords[] = { offsetof (struct component, flags), _cb_precious, }, + {"wait", + NULL, + N_("Wait for the server program to return."), + grecs_type_bool, NULL, + offsetof (struct component, flags), + _cb_wait, + }, {"max-instances", NULL, N_("Maximum number of running instances."), @@ -859,6 +904,12 @@ struct grecs_keyword component_keywords[] = { offsetof (struct component, socket_url), _cb_url, }, + {"socket-type", + N_("type: stream | dgram | raw | rdm | seqpacket"), + N_("Set socket type."), + grecs_type_int, NULL, + offsetof (struct component, socket_type), + _cb_socket_type }, {"pass-fd-socket", N_("name"), N_("Pass fd through this socket."), @@ -978,7 +1029,7 @@ make_full_name (const char *dir, const char *file) } static int -component_verify (struct component *comp, grecs_locus_t * locus) +component_verify (struct component *comp, grecs_locus_t *locus) { int header = 0; int i; @@ -1013,7 +1064,7 @@ component_verify (struct component *comp, grecs_locus_t * locus) if (comp->dir) { char *p = make_full_name (comp->dir, comp->pass_fd_socket); - //free (comp->pass_fd_socket); + /*free (comp->pass_fd_socket);*/ comp->pass_fd_socket = p; } else @@ -1032,6 +1083,22 @@ component_verify (struct component *comp, grecs_locus_t * locus) } } + if (comp->mode == pies_comp_inetd) + { + if ((comp->flags & CF_WAIT) && comp->socket_type == SOCK_STREAM) + { + if (comp->max_instances) + COMPERR ("%s", _("max-instances ignored")); + else + comp->max_instances = 1; + } + } + else if (comp->flags & CF_WAIT) + { + COMPERR ("%s", _("wait is useless in this mode")); + comp->flags &= ~CF_WAIT; + } + if (comp->mode != pies_comp_exec && comp->redir[RETR_OUT].type != redir_null) { @@ -1071,6 +1138,7 @@ component_create (const char *name) comp->facility = log_facility; comp->redir[RETR_OUT].type = comp->redir[RETR_ERR].type = redir_null; comp->tag = xstrdup (name); + comp->socket_type = SOCK_STREAM; } return comp; } diff --git a/src/pies.h b/src/pies.h index 02211a2..68a2ff5 100644 --- a/src/pies.h +++ b/src/pies.h @@ -137,7 +137,9 @@ enum pies_comp_mode }; #define CF_DISABLED 0x01 /* The componenet is disabled */ -#define CF_PRECIOUS 0x02 /* The component is precious (cannot be disabled) */ +#define CF_PRECIOUS 0x02 /* The component is precious (should not + be disabled) */ +#define CF_WAIT 0x04 struct component { @@ -152,6 +154,7 @@ struct component gl_list_t depend; /* Dependency targets */ int flags; /* CF_ bitmask */ size_t max_instances; /* Maximum number of running instances */ + int socket_type; /* Socket type */ char *rmfile; /* Try to remove this file before starting */ struct pies_privs privs; /* UID/GIDS+groups to run under */ mode_t umask; /* Umask to install before starting */ @@ -229,9 +232,12 @@ struct grecs_keyword *find_component_keyword (const char *ident); struct pies_url { char *string; - char *proto; + char *scheme; char *host; + char *port_s; int port; + char *proto_s; + int proto; char *path; char *user; char *passwd; @@ -244,9 +250,12 @@ void pies_url_destroy (struct pies_url **purl); const char * pies_url_get_arg (struct pies_url *url, const char *argname); void pies_pause (void); -int register_listener (int fd); +int register_socket (int socktype, int fd); int pass_fd (const char *socket, int fd, unsigned time_out); -int create_socket (struct pies_url *url, const char *user, mode_t umask); +int create_socket (struct pies_url *url, int socket_type, + const char *user, mode_t umask); +int disable_socket (int fd); +int enable_socket (int fd); int parse_limits (limits_record_t *plrec, char *str, char **endp); diff --git a/src/progman.c b/src/progman.c index 3263172..85a5068 100644 --- a/src/progman.c +++ b/src/progman.c @@ -388,6 +388,19 @@ redirect_to_file (struct prog *master, int stream) return fd; } +static void +close_fds (fd_set *fdset) +{ + int i; + + for (i = getmaxfd (); i >= 0; i--) + { + if (fdset && FD_ISSET (i, fdset)) + continue; + close (i); + } +} + int open_redirector (struct prog *master, int stream) { @@ -398,6 +411,7 @@ open_redirector (struct prog *master, int stream) pid_t pid; int i, prio; char *tag; + fd_set fdset; switch (master->v.p.comp->redir[stream].type) { @@ -420,11 +434,9 @@ open_redirector (struct prog *master, int stream) mf_proctitle_format ("%s redirector", tag); free (tag); - for (i = getmaxfd (); i >= 0; i--) - { - if (i != p[0]) - close (i); - } + FD_ZERO (&fdset); + FD_SET (p[0], &fdset); + close_fds (&fdset); diag_setup (0); signal_setup (redir_exit); @@ -605,7 +617,8 @@ prog_start (struct prog *prog) pid_t pid; time_t now; int redir[2]; - + fd_set fdset; + if (prog->pid > 0 || !IS_COMPONENT (prog)) return; @@ -658,6 +671,7 @@ prog_start (struct prog *prog) case pies_comp_accept: prog->v.p.socket = create_socket (prog->v.p.comp->socket_url, + prog->v.p.comp->socket_type, prog->v.p.comp->privs.user, prog->v.p.comp->umask); if (prog->v.p.socket == -1) @@ -771,14 +785,14 @@ prog_start (struct prog *prog) dup2 (redir[RETR_ERR], 2); } - /* Close unneded descripitors */ - for (i = getmaxfd (); i > 2; i--) - { - if (prog->v.p.comp->mode == pies_comp_pass_fd - && i == prog->v.p.socket) - continue; - close (i); - } + /* Close unneeded descripitors */ + FD_ZERO (&fdset); + FD_SET (0, &fdset); + FD_SET (1, &fdset); + FD_SET (2, &fdset); + if (prog->v.p.comp->mode == pies_comp_pass_fd) + FD_SET (prog->v.p.socket, &fdset); + close_fds (&fdset); execvp (prog->v.p.comp->program ? prog->v.p.comp->program : prog->v.p.comp->argv[0], @@ -802,7 +816,11 @@ prog_start (struct prog *prog) prog->v.p.comp->pass_fd_timeout : DEFAULT_PASS_FD_TIMEOUT); /* FIXME: Error code */; } - if (prog->v.p.comp->mode != pies_comp_exec) + if (prog->v.p.comp->flags & CF_WAIT) + { + disable_socket (prog->v.p.socket); + } + else if (prog->v.p.comp->mode != pies_comp_exec) close (prog->v.p.socket); prog->pid = pid; prog->v.p.status = status_enabled; @@ -836,38 +854,29 @@ check_acl (pies_acl_t acl, struct sockaddr *s, int salen) return 0; } -int -progman_accept (int socket) +static int +_prog_accept (struct prog *p) { int fd; - struct prog *p, *pinst; + struct prog *pinst; union { struct sockaddr s_in; struct sockaddr s_un; } addr; socklen_t addrlen = sizeof addr; - - fd = accept (socket, (struct sockaddr*) &addr, &addrlen); + + fd = accept (p->v.p.socket, (struct sockaddr*) &addr, &addrlen); if (fd == -1) { logmsg (LOG_ERR, _("accept failed: %s"), strerror (errno)); return 1; } - p = prog_lookup_by_socket (socket); - if (!p) - { - logmsg (LOG_EMERG, - _("INTERNAL ERROR: no matching prog for fd %d"), socket); - close (fd); - return 1; - } - if (debug_level >= 1) { char *s = sockaddr_to_astr ((struct sockaddr *)&addr, addrlen); - logmsg (LOG_DEBUG, "%s wants %s", s, p->tag); + logmsg (LOG_DEBUG, _("%s wants %s"), s, p->tag); free (s); } @@ -901,6 +910,51 @@ progman_accept (int socket) return 0; } +static int +_prog_wait (struct prog *p) +{ + struct prog *pinst; + + debug (1, (_("someone wants %s"), p->tag)); + + if (p->v.p.comp->max_instances + && p->v.p.num_instances >= p->v.p.comp->max_instances) + { + logmsg (LOG_ERR, + _("%s: too many instances running, dropping connection"), + p->tag); + return 1; + } + + pinst = register_prog0 (p->v.p.comp, -1); + pinst->v.p.socket = p->v.p.socket; + pinst->v.p.listener = p; + prog_start (pinst); + + pinst->v.p.socket = -1; + + p->v.p.num_instances++; + return 0; +} + +int +progman_accept (int socket) +{ + struct prog *p = prog_lookup_by_socket (socket); + if (!p) + { + logmsg (LOG_EMERG, + _("INTERNAL ERROR: no matching prog for fd %d"), socket); + return 1; + } + + if (p->v.p.comp->socket_type == SOCK_STREAM + && !(p->v.p.comp->flags & CF_WAIT)) + return _prog_accept (p); + + return _prog_wait (p); +} + void component_fixup_depend (struct component *comp) @@ -1077,10 +1131,11 @@ progman_create_sockets () if (comp->mode == pies_comp_inetd) { int fd = create_socket (comp->socket_url, + comp->socket_type, comp->privs.user, comp->umask); if (fd == -1) prog->v.p.status = status_disabled; - else if (register_listener (fd)) + else if (register_socket (comp->socket_type, fd)) { close (fd); prog->v.p.status = status_disabled; @@ -1664,8 +1719,7 @@ run_command (struct action *act, struct prog *prog, unsigned retcode, setenv ("PIES_STATUS", umaxtostr (STATUS_CODE (retcode), buf), 1); /* FIXME: see above */ - for (i = getmaxfd (); i >= 0; i--) - close (i); + close_fds (NULL); argv[0] = "/bin/sh"; argv[1] = "-c"; @@ -1707,9 +1761,13 @@ progman_cleanup (int expect_term) print_status (prog->tag, pid, status, expect_term); if (prog->v.p.comp->mode == pies_comp_inetd) { - prog->v.p.listener->v.p.num_instances--; + struct prog *listener = prog->v.p.listener; + + listener->v.p.num_instances--; prog_stop_redirectors (prog); destroy_prog (&prog); + if (listener->v.p.comp->flags & CF_WAIT) + enable_socket (listener->v.p.socket); } else { diff --git a/src/socket.c b/src/socket.c index 6f1bb87..8b25073 100644 --- a/src/socket.c +++ b/src/socket.c @@ -49,7 +49,8 @@ switch_eids (uid_t *puid, gid_t *pgid, mode_t *pumask) } int -create_socket (struct pies_url *url, const char *user, mode_t umaskval) +create_socket (struct pies_url *url, int socket_type, + const char *user, mode_t umaskval) { int rc; int fd; @@ -64,9 +65,9 @@ create_socket (struct pies_url *url, const char *user, mode_t umaskval) gid_t gid = 0; int switch_back; - if (strcmp (url->proto, "unix") == 0 - || strcmp (url->proto, "file") == 0 - || strcmp (url->proto, "socket") == 0) + if (strcmp (url->scheme, "unix") == 0 + || strcmp (url->scheme, "file") == 0 + || strcmp (url->scheme, "socket") == 0) { struct stat st; const char *group = NULL; @@ -169,7 +170,7 @@ create_socket (struct pies_url *url, const char *user, mode_t umaskval) } } } - else if (strcmp (url->proto, "inet") == 0) + else if (strcmp (url->scheme, "inet") == 0) { const char *host = url->host; short port = url->port; @@ -209,7 +210,7 @@ create_socket (struct pies_url *url, const char *user, mode_t umaskval) return -1; } - fd = socket (addr.sa.sa_family, SOCK_STREAM, 0); + fd = socket (addr.sa.sa_family, socket_type, url->proto); if (fd == -1) { logmsg (LOG_ERR, _("%s: cannot create socket: %s"), @@ -409,9 +410,9 @@ fd_set listenset; int fd_max; int -register_listener (int fd) +register_socket (int socktype, int fd) { - if (listen (fd, 8) == -1) + if (socktype == SOCK_STREAM && listen (fd, 8) == -1) { logmsg (LOG_ERR, "listen: %s", strerror (errno)); return 1; @@ -422,6 +423,20 @@ register_listener (int fd) return 0; } +int +disable_socket (int fd) +{ + debug (2, (_("disabling fd %d"), fd)); + FD_CLR (fd, &listenset); +} + +int +enable_socket (int fd) +{ + debug (2, (_("enabling fd %d"), fd)); + FD_SET (fd, &listenset); +} + void pies_pause () { @@ -437,6 +452,7 @@ pies_pause () if (FD_ISSET (i, &rdset)) progman_accept (i); } + break; } else if (rc < 0) { diff --git a/src/url.c b/src/url.c index 500aa0a..be74974 100644 --- a/src/url.c +++ b/src/url.c @@ -18,11 +18,12 @@ # include #endif #include "pies.h" +#include #include #include #include -/* proto://[user[:password]@][host[:port]/]path[;arg=str[;arg=str...] +/* scheme://[user[:password]@][host[:port]/]path[;arg=str[;arg=str...] */ static int @@ -59,17 +60,17 @@ url_parse_args (struct pies_url *url, char **str) static int url_parse_path (struct pies_url *url, char **str) { - char *p; - - p = strchr (*str, ';'); - if (!p) - p = *str + strlen (*str); - if (alloc_string(&url->path, *str, p)) - return 1; - *str = p; - if (*p) - ++ * str; - return url_parse_args (url, str); + char *p; + + p = strchr (*str, ';'); + if (!p) + p = *str + strlen (*str); + if (alloc_string (&url->path, *str, p)) + return 1; + *str = p; + if (*p) + ++*str; + return url_parse_args (url, str); } /* On input str points at the beginning of host part */ @@ -81,12 +82,29 @@ url_parse_host (struct pies_url *url, char **str) if (s[len] == ':') { + char *start = s + len + 1; char *q; - unsigned long n = strtoul (s + len + 1, &q, 10); - if ((*q && !strchr("/;:", *q)) || n > USHRT_MAX) + unsigned long n = strtoul (start, &q, 10); + if (n > USHRT_MAX) return 1; - url->port = n; - *str = q + strcspn(q, "/"); + if ((*q && !strchr ("/;:", *q))) + { + char *proto = url->proto_s ? url->proto_s : "tcp"; + size_t size = strcspn (start, "/;:"); + struct servent *serv; + + alloc_string_len (&url->port_s, start, size); + serv = getservbyname (url->port_s, proto); + if (!serv) + return 1; + url->port = ntohs (serv->s_port); + *str = start + size; + } + else + { + url->port = n; + *str = q; + } } else *str = s + len; @@ -106,22 +124,22 @@ url_parse_user (struct pies_url *url, char **str) { size_t len = strcspn (*str, ":;@/"); char *p = *str + len; - + switch (*p) { case ';': case ':': len = strcspn (p + 1, "@/:"); - if (p[len+1] == '@') + if (p[len + 1] == '@') { - if (alloc_string_len(&url->passwd, p + 1, len)) + if (alloc_string_len (&url->passwd, p + 1, len)) return 1; if (alloc_string (&url->user, *str, p)) return 1; *str = p + len + 2; } break; - + case '@': if (alloc_string (&url->user, *str, p)) return 1; @@ -132,27 +150,53 @@ url_parse_user (struct pies_url *url, char **str) } static int -url_parse_proto (struct pies_url *url, const char *str) +url_parse_scheme (struct pies_url *url, const char *str) { + size_t len; char *p; - + if (!str) { errno = EINVAL; return 1; } - - p = strchr (str, ':'); - if (!p) + + len = strcspn (str, ":+"); + if (!str[len]) { errno = EINVAL; return 1; } - - alloc_string (&url->proto, str, p); + alloc_string_len (&url->scheme, str, len); + + str += len; + + if (*str == '+') + { + struct protoent *proto; + + len = strcspn (++str, ":"); + if (str[len] == 0) + { + errno = EINVAL; + return 1; + } + alloc_string_len (&url->proto_s, str, len); + str += len; + proto = getprotobyname (url->proto_s); + if (!proto) + { + errno = EINVAL; + return 1; + } + url->proto = proto->p_proto; + } + else + url->proto = 0; + /* Skip slashes */ - for (p++; *p == '/'; p++) + for (p = (char*) str + 1; *p == '/'; p++) ; return url_parse_user (url, &p); } @@ -164,15 +208,17 @@ pies_url_destroy (struct pies_url **purl) struct pies_url *url = *purl; free (url->string); - free (url->proto); + free (url->scheme); free (url->host); + free (url->port_s); + free (url->proto_s); free (url->path); free (url->user); free (url->passwd); for (i = 0; i < url->argc; i++) free (url->argv[i]); free (url->argv); - free(url); + free (url); *purl = NULL; } @@ -181,12 +227,12 @@ pies_url_create (struct pies_url **purl, const char *str) { int rc; struct pies_url *url; - + url = malloc (sizeof (*url)); if (!url) return 1; - memset (url, 0, sizeof(*url)); - rc = url_parse_proto (url, str); + memset (url, 0, sizeof (*url)); + rc = url_parse_scheme (url, str); if (rc) pies_url_destroy (&url); else -- cgit v1.2.1