1 /* Generic server process.
2 * - by Richard W.M. Jones <rich@annexia.org>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the Free
16 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 * $Id: pthr_server.c,v 1.10 2003/02/05 22:13:33 rich Exp $
38 #ifdef HAVE_SYS_TYPES_H
39 #include <sys/types.h>
42 #ifdef HAVE_SYS_SOCKET_H
43 #include <sys/socket.h>
46 #ifdef HAVE_NETINET_IN_H
47 #include <netinet/in.h>
50 #ifdef HAVE_ARPA_INET_H
51 #include <arpa/inet.h>
66 #ifdef HAVE_EXECINFO_H
74 #include "pthr_listener.h"
75 #include "pthr_server.h"
77 static int default_port = 80;
78 static char port_option_name = 'p';
79 static in_addr_t default_address = INADDR_ANY;
80 static char address_option_name = 'a';
81 static int disable_syslog = 0;
82 static const char *package_name = PACKAGE " " VERSION;
83 static int disable_fork = 0;
84 static int disable_chdir = 0;
85 static int disable_close = 0;
86 static const char *root = 0;
87 static const char *username = 0;
88 static const char *stderr_file = 0;
89 static void (*startup_fn)(int argc, char *argv[]) = 0;
90 static int enable_stack_trace_on_segv = 0;
92 #if defined(HAVE_EXECINFO_H) && defined(HAVE_BACKTRACE)
93 #define CAN_CATCH_SIGSEGV
96 #ifdef CAN_CATCH_SIGSEGV
97 static void catch_sigsegv (int);
109 #define INADDR_NONE ((in_addr_t) 0xffffffff)
113 pthr_server_main_loop (int argc, char *argv[],
114 void (*processor_fn) (int sock, void *))
116 struct sockaddr_in addr;
117 int port = default_port;
118 in_addr_t address = default_address;
123 /* Reset the getopt library. */
131 /* Handle command-line arguments. Get the correct port and address to
134 snprintf (getopt_scr, sizeof getopt_scr,
135 "%c:%c:", port_option_name, address_option_name);
137 while ((c = getopt (argc, argv, getopt_scr)) != -1)
139 if (port_option_name == c)
141 if (sscanf (optarg, "%d", &port) != 1)
143 fprintf (stderr, "invalid port option: %s\n", optarg);
147 else if (address_option_name == c)
149 /* Should really use inet_aton() (or even inet_pton())
150 * but inet_addr() is more widely supported.
152 address = inet_addr (optarg);
153 if (INADDR_NONE == address)
155 fprintf (stderr, "invalid address: %s\n", optarg);
161 /* Bind a socket to the appropriate port. */
162 sock = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP);
163 if (sock < 0) abort ();
165 setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, &one, sizeof (one));
167 memset(&addr, 0, sizeof(addr));
168 addr.sin_family = AF_INET;
169 addr.sin_addr.s_addr = address;
170 addr.sin_port = htons (port);
171 if (bind (sock, (struct sockaddr *) &addr, sizeof addr) < 0)
173 /* Generally this means that the port is already bound. */
178 /* Put the socket into listen mode. */
179 if (listen (sock, 10) < 0) abort ();
181 /* Set the new socket to non-blocking. */
182 if (fcntl (sock, F_SETFL, O_NONBLOCK) < 0) abort ();
184 /* If running as root, and asked to chroot, then chroot. */
185 if (root && geteuid () == 0)
187 if (chroot (root) == -1)
194 /* If running as root, and asked to change user, do so now. */
195 if (username && geteuid () == 0)
197 struct passwd *pw = getpwnam (username);
201 fprintf (stderr, "username not found: %s", username);
205 if (initgroups (username, pw->pw_gid) == -1 ||
206 setgid (pw->pw_gid) == -1 ||
207 setuid (pw->pw_uid) == -1)
219 /* Close connections to the terminal. */
223 open ("/dev/null", O_RDWR);
231 /* Reopen stderr on a log file. */
233 if (open (stderr_file, O_WRONLY|O_CREAT|O_APPEND, 0644) == -1)
235 /* Hard to output an error message at this point ... */
242 /* Fork into background. */
245 if (pid < 0) { perror ("fork"); exit (1); }
248 /* Parent process: exit normally. */
255 /* Open connection to syslog. */
256 openlog (package_name, LOG_PID|LOG_NDELAY, LOG_USER);
258 "%s starting up on port %d", package_name, port);
261 if (enable_stack_trace_on_segv)
263 #ifdef CAN_CATCH_SIGSEGV
266 /* Print a stack trace to stderr if we get SIGSEGV. */
267 memset (&sa, 0, sizeof sa);
268 sa.sa_handler = catch_sigsegv;
270 sigaction (SIGSEGV, &sa, 0);
274 /* Run the startup function, if any. */
276 startup_fn (argc, argv);
278 /* Start the listener thread. */
279 (void) new_listener (sock, processor_fn, 0);
281 /* Run the reactor. */
282 while (pseudothread_count_threads () > 0)
286 #ifdef CAN_CATCH_SIGSEGV
288 catch_sigsegv (int sig)
291 const char msg[] = "** Segmentation fault **\n\nStack trace:\n\n";
292 void *addr[MAX_ADDRS];
295 write (2, msg, sizeof (msg) - 1);
297 /* Write the stack trace to stderr. */
298 n = backtrace (addr, MAX_ADDRS);
299 backtrace_symbols_fd (addr, n, 2);
308 pthr_server_default_port (int _default_port)
310 default_port = _default_port;
314 pthr_server_port_option_name (char _port_option_name)
316 port_option_name = _port_option_name;
320 pthr_server_default_address (in_addr_t _default_address)
322 default_address = _default_address;
326 pthr_server_address_option_name (char _address_option_name)
328 address_option_name = _address_option_name;
332 pthr_server_disable_syslog (void)
338 pthr_server_package_name (const char *_package_name)
340 package_name = _package_name;
344 pthr_server_disable_fork (void)
350 pthr_server_disable_chdir (void)
356 pthr_server_disable_close (void)
362 pthr_server_chroot (const char *_root)
368 pthr_server_username (const char *_username)
370 username = _username;
374 pthr_server_stderr_file (const char *_pathname)
376 stderr_file = _pathname;
380 pthr_server_startup_fn (void (*_startup_fn) (int argc, char *argv[]))
382 startup_fn = _startup_fn;
386 pthr_server_enable_stack_trace_on_segv (void)
388 enable_stack_trace_on_segv = 1;