From 5e37235d8726bf81ef8a343c62dc0b06de344337 Mon Sep 17 00:00:00 2001 From: Igor Pashev Date: Sat, 18 Jan 2014 17:26:45 +0400 Subject: Initial version --- main.c | 273 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 273 insertions(+) create mode 100644 main.c (limited to 'main.c') diff --git a/main.c b/main.c new file mode 100644 index 0000000..3a42639 --- /dev/null +++ b/main.c @@ -0,0 +1,273 @@ +/* +Copyright (c) 2014 Igor Pashev + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#ifdef ENABLE_CGROUPS +#include +#endif + +#include "dispatch.h" +#include "uri.h" +#include "debug.h" + +static const char *progname; + +/* Tunable parameters: */ +static int number_of_workers = 5; +static const char *socket_path = ":9000"; +static int backlog = 16; + +static pthread_mutex_t accept_mutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_t *pthread_ids = NULL; +static int socket = -1; + + +static void * +worker (void *param) +{ + FCGX_Request request; + + debug ("thread #%" PRIdPTR " started", (intptr_t) param); + if (0 != FCGX_InitRequest (&request, socket, /* int flags */ 0)) + { + return (NULL); + } + + while (1) + { + pthread_mutex_lock (&accept_mutex); + int rc = FCGX_Accept_r (&request); + pthread_mutex_unlock (&accept_mutex); + debug ("thread #%" PRIdPTR " accepted request", (intptr_t) param); + + if (rc < 0) + { + debug ("thread #%" PRIdPTR " FCGX_Accept_r() failed: %s", + (intptr_t) param, strerror (errno)); + break; + } + + dispatch (&request); + + FCGX_Finish_r (&request); + } + return NULL; +} + + +static void +version (void) +{ + printf (PACKAGE " " VERSION "\n"); +#ifdef HAVE_XXD + extern unsigned char LICENSE[]; + extern unsigned int LICENSE_len; + printf ("%.*s\n", LICENSE_len, LICENSE); +#endif + printf ("Report bugs to <" PACKAGE_BUGREPORT ">\n"); + exit (0); +} + + +static void +usage (void) +{ + printf ("Usage: %s [options]\n", progname); + printf ("FastCGI REST server\n\n"); + printf + ("Mandatory arguments to long options are mandatory for short options too.\n"); + printf (" -s, --socket={path|:port} a socket or a port number (%s)\n", + socket_path); + printf (" -b, --backlog=number listen queue depth (%d)\n", backlog); + printf (" -w, --threads=number number of threads to run (%d)\n", + number_of_workers); + printf (" -u, --uri-prefix=string URI prefix to trim (%s)\n", + uri_prefix); + printf (" -h, --help show this help message\n"); + printf (" -v, --version show version\n"); + exit (0); +} + + +static void +parse_options (int argc, char **argv) +{ + static const char *short_options = "s:b:w:u:hv"; + + static const struct option long_options[] = { + {"socket", required_argument, NULL, 's'}, + {"backlog", required_argument, NULL, 'b'}, + {"threads", required_argument, NULL, 'w'}, + {"uri-prefix", required_argument, NULL, 'u'}, + {"help", no_argument, NULL, 'h'}, + {"version", no_argument, NULL, 'v'}, + {NULL, 0, NULL, 0} + }; + + progname = argv[0]; + + int option_index; + int opt; + while ((opt = getopt_long (argc, argv, short_options, + long_options, &option_index)) != -1) + switch (opt) + { + case 's': + socket_path = optarg; + break; + case 'b': + backlog = atoi (optarg); + if (backlog <= 0) + { + fprintf (stderr, + "%s: backlog number must be a positive integer\n", + progname); + exit (1); + } + break; + case 'w': + number_of_workers = atoi (optarg); + if (number_of_workers <= 0) + { + fprintf (stderr, + "%s: number of workers must be a positive integer\n", + progname); + exit (1); + } + break; + case 'u': + uri_prefix = optarg; + uri_prefix_len = strlen (uri_prefix); + break; + case 'h': + usage (); + break; + case 'v': + version (); + break; + default: + fprintf (stderr, "Use `%s --help' to get help\n", progname); + exit (1); + break; + } +} + + +static void +init_libraries (void) +{ + int rc; + debug ("initializing libfcgi"); + if (0 != (rc = FCGX_Init ())) + { + fprintf (stderr, "%s: FCGX_Init() failed. Exiting.\n", progname); + exit (EXIT_FAILURE); + } +#ifdef ENABLE_CGROUPS + debug ("initializing libcgroup"); + if (0 != (rc = cgroup_init ())) + { + fprintf (stderr, "%s: cgroup_init() failed. Exiting.\n", progname); + exit (EXIT_FAILURE); + } +#endif +} + + +int +main (int argc, char **argv) +{ + parse_options (argc, argv); + + init_libraries (); + + fprintf (stderr, + "%s: socket `%s', backlog %d, %d worker%s, URI prefix `%s'\n", + progname, socket_path, backlog, number_of_workers, + (number_of_workers == 1 ? "" : "s"), uri_prefix); + + socket = FCGX_OpenSocket (socket_path, backlog); + if (socket < 0) + { + fprintf (stderr, "%s: FCGX_OpenSocket() failed: %s. Exiting.\n", + progname, strerror (errno)); + return (EXIT_FAILURE); + } + + debug ("allocating space for %d threads", number_of_workers); + pthread_ids = (pthread_t *) malloc (sizeof (pthread_t) * number_of_workers); + if (NULL == pthread_ids) + { + fprintf (stderr, "%s: malloc() failed: %s. Exiting.\n", progname, + strerror (errno)); + return (EXIT_FAILURE); + } + + debug ("starting threads"); + for (int thr = 0; thr < number_of_workers; ++thr) + { + int rc; + do + { + debug ("starting thread #%d", thr); + errno = 0; + rc = + pthread_create (&(pthread_ids[thr]), NULL, worker, + (void *) ((intptr_t) thr)); + } + while ((0 != rc) && (EAGAIN == errno)); + + if (0 != rc) + { + fprintf (stderr, "%s: pthread_create() failed: %s. Exiting.\n", + progname, strerror (errno)); + return (EXIT_FAILURE); + } + } + + for (int thr = 0; thr < number_of_workers; ++thr) + { + int rc = pthread_join (pthread_ids[thr], NULL); + if (0 != rc) + { + fprintf (stderr, "%s: pthread_join() failed: %s. Exiting.\n", + progname, strerror (errno)); + return (EXIT_FAILURE); + } + } + + return (EXIT_SUCCESS); +} -- cgit v1.2.3