crossroads

Git mirror of https://crossroads.e-tunity.com/
git clone git://git.finwo.net/app/crossroads
Log | Files | Refs

commit 6b70b8c84664f605bf793999e3bb4633bd67a9d4
parent 2fcfb7de1fd14ee9777829b7f873b55b2bb3b313
Author: finwo <finwo@pm.me>
Date:   Sat,  3 Jan 2026 19:07:54 +0100

1.06

Diffstat:
MChangeLog | 27+++++++++++++++++++++++++++
Mdoc/compiling.yo | 2+-
Mdoc/crossroads.html | 8++++----
Mdoc/crossroads.man | 6+++---
Mdoc/crossroads.pdf | 0
Mdoc/crossroads.yo | 2+-
Mdoc/using.yo | 2+-
Metc/Makefile.def | 4++--
Msrc/Makefile | 3++-
Msrc/allocreporter.c | 11-----------
Msrc/backendavailable.c | 3++-
Msrc/choosebackend.c | 29++++++++++++++++++++---------
Msrc/copysockets.c | 30+++++++++++-------------------
Msrc/crossroads.h | 3+--
Msrc/deallocreporter.c | 39++++++++++++++++++++-------------------
Msrc/forktcpservicer.c | 3---
Msrc/httpread.c | 4----
Msrc/httpserve.c | 18++++++++++--------
Msrc/interrupt.c | 33+++++++++++++++++++--------------
Msrc/lockreporter.c | 3---
Msrc/main.c | 11+----------
Msrc/makesocket.c | 19++++++++++++-------
Msrc/markactivity.c | 3+--
Msrc/restart.c | 5+++--
Msrc/runservice.c | 97+++++++++++++++++++++++++++++++++++++++----------------------------------------
Msrc/sample.conf | 57++++++++++++++++++++++++++++-----------------------------
Msrc/serve.c | 6+-----
Msrc/tcpserve.c | 29+++++++++++++++++------------
Msrc/unlockreporter.c | 3---
Msrc/usage.txt | 21+++++----------------
Msrc/xmalloc.c | 6+-----
Msrc/xrealloc.c | 22++++++++++++++++------
Atest/client | 56++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/server | 94+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/t1.conf | 36++++++++++++++++++++++++++++++++++++
Atest/t2.conf | 36++++++++++++++++++++++++++++++++++++
Atest/t3.conf | 13+++++++++++++
Mtools/c-conf | 27++++++++++++++++-----------
38 files changed, 508 insertions(+), 263 deletions(-)

diff --git a/ChangeLog b/ChangeLog @@ -1,6 +1,32 @@ ChangeLog for Crossroads ------------------------------------------------------------------------------ +1.06 [KK 2006-07-25] Small documentation fix. Embedded c-conf upped to + 1.03 (protection from endless recursive directory + scans). Optimized http_serve() for short messages (no + piggybacking is necessary after the initial chat). Flag -u (user + switch) removed; user switching won't work given privileged TCP + ports, shared memory access and semaphores all mixed together (or + in other words: I'm too lazy right now to make this work + elegantly.) + Version 1.06 promoted to the next stable. + +1.05 [KK 2006-07-12] Restarting heavily sped up, plus it won't affect + creation of new listener sockets. Messaging improved, the + affected service is reported. Flag -f (foreground mode) removed; + this debugging option is no longer useful given the complexity of + it all. Added socket shutdowns to interrupt handler + (interrupt.c). Fixed up shared memory free ups. + THIS VERSION SHOULD MAKE "CROSSROADS RESTART" WORK WELL! + +1.04 [KK 2006-06-26] Next development version. + [KK 2006-07-03] Fixed http_read(); the # of historic + connections ('totuses') would be counted double in sticky HTTP + mode. + [KK 2006-07-04] Added call to sranddev() to make weighted random + dispatching more conforming to the weights (HAVE_SRANDDEV defined + in the Makefile when sranddev() is available). + 1.03 [KK 2006-06-20] Support for older gcc's (incase strlcat() is missing; added strlcat.c). Added -lm to linkage flags for fmod(). @@ -11,6 +37,7 @@ ChangeLog for Crossroads the old code wouldn't correctly see similar high weights. [KK 2006-06-26] Fixed counting of total nr. of connections in sticky HTTP mode (as shown with 'crossroads status'). + This version is tagged as 'stable'. 1.02 [KK 2006-05-11] Next development release. [KK 2006-05-12] In HTTP mode, the nr of sessions per back end is diff --git a/doc/compiling.yo b/doc/compiling.yo @@ -148,7 +148,7 @@ verb(\ controlled by the appropriate statements in the configuration. it() Determine your 'runlevel': usually 3 when your system is - running in text-mode only, or 5 when you are a graphical + running in text-mode only, or 5 when you are using a graphical interface. If your runlevel is 3, then: verb(\ diff --git a/doc/crossroads.html b/doc/crossroads.html @@ -1,12 +1,12 @@ <a name="defs.yo"></a><html><head> -<title>Crossroads 1.03</title> +<title>Crossroads 1.05</title> <link rel="stylesheet" type="text/css" href="http://www.e-tunity.com/css/yodl.css"> <link rel="stylesheet" type="text/css" href="http://www.e-tunity.com/css/yodl.css"> <link rev="made" href="mailto:info@e-tunity.com"> </head> <body> <hr> -<h1>Crossroads 1.03</h1> +<h1>Crossroads 1.05</h1> <h2>Karel Kubat</h2> <h2>e-tunity</h2><h2>2005, 2006, ff.</h2> @@ -17,7 +17,7 @@ back ends using 'wakeup calls', detailed status reporting, 'hooks' for special actions when backend calls fail, and much more. Crossroads is service-independent: it is usable for - HTTP(S), SSH, SMTP, DNS, etc. In the case of HTTP + HTTP/HTTPS, SSH, SMTP, DNS, etc. In the case of HTTP balancing, Crossroads can provide 'session stickiness' for back-end processes that need sessions, but aren't session-aware of other back-ends.</em></blockquote> @@ -400,7 +400,7 @@ determining your system load. Second, there's flag <code>-l</code>. This flag selects the 'facility' of logging and defaults to <code>LOG_DAEMON</code>. You can supply a number between 0 and 7 to flag <code>-l</code> to select <code>LOG_LOCAL0</code> to -ttt(LOG_LOCAL7). This would separate the Crossroads-related logging +<code>LOG_LOCAL7</code>. This would separate the Crossroads-related logging from other streams. Here's a very short guide; please read your Unix manpages of <code>syslogd</code> for more information. <p> diff --git a/doc/crossroads.man b/doc/crossroads.man @@ -1,6 +1,6 @@ -.TH "Crossroads 1\&.03" "2005, 2006, ff\&." +.TH "Crossroads 1\&.05" "2005, 2006, ff\&." .PP -.SH "Crossroads 1\&.03" +.SH "Crossroads 1\&.05" .SH "Karel Kubat" .SH "e-tunity" .SH "2005, 2006, ff\&." @@ -346,7 +346,7 @@ determining your system load\&. Second, there\&'s flag \f(CW-l\fP\&. This flag selects the \&'facility\&' of logging and defaults to \f(CWLOG_DAEMON\fP\&. You can supply a number between 0 and 7 to flag \f(CW-l\fP to select \f(CWLOG_LOCAL0\fP to -ttt(LOG_LOCAL7)\&. This would separate the Crossroads-related logging +\f(CWLOG_LOCAL7\fP\&. This would separate the Crossroads-related logging from other streams\&. Here\&'s a very short guide; please read your Unix manpages of \f(CWsyslogd\fP for more information\&. .PP diff --git a/doc/crossroads.pdf b/doc/crossroads.pdf Binary files differ. diff --git a/doc/crossroads.yo b/doc/crossroads.yo @@ -7,7 +7,7 @@ abstract(Crossroads is a load balance and fail over utility for TCP back ends using 'wakeup calls', detailed status reporting, 'hooks' for special actions when backend calls fail, and much more. Crossroads is service-independent: it is usable for - HTTP(S), SSH, SMTP, DNS, etc. In the case of HTTP + HTTP/HTTPS, SSH, SMTP, DNS, etc. In the case of HTTP balancing, Crossroads can provide 'session stickiness' for back-end processes that need sessions, but aren't session-aware of other back-ends.) diff --git a/doc/using.yo b/doc/using.yo @@ -55,7 +55,7 @@ determining your system load. Second, there's flag tt(-l). This flag selects the 'facility' of logging and defaults to tt(LOG_DAEMON). You can supply a number between 0 and 7 to flag tt(-l) to select tt(LOG_LOCAL0) to -ttt(LOG_LOCAL7). This would separate the Crossroads-related logging +tt(LOG_LOCAL7). This would separate the Crossroads-related logging from other streams. Here's a very short guide; please read your Unix manpages of tt(syslogd) for more information. diff --git a/etc/Makefile.def b/etc/Makefile.def @@ -3,14 +3,14 @@ # Versioning. This defines the overall version ID and must match the topmost # entry in the ChangeLog. -VER = 1.03 +VER = 1.06 # Default config DEFAULT_CONF = /etc/crossroads.conf # The max nr of backends of a given service. This is a fixed number, # because it defines the size of the shared memory that crossroads claims. -MAX_BACKEND = 50 +MAX_BACKEND = 10 # Magic mask for shared memory keys. SHM_MASK = 0x19081962 diff --git a/src/Makefile b/src/Makefile @@ -43,7 +43,8 @@ DEFS = -DDEFAULT_CONF=\"$(DEFAULT_CONF)\" -DMAX_BACKEND=$(MAX_BACKEND) \ $(shell ../tools/c-conf ifheader stdint.h HAVE_STDINT_H) \ $(shell ../tools/c-conf libfunction flock HAVE_FLOCK) \ $(shell ../tools/c-conf libfunction lockf HAVE_LOCKF) \ - $(shell ../tools/c-conf libfunction strlcat HAVE_STRLCAT) + $(shell ../tools/c-conf libfunction strlcat HAVE_STRLCAT) \ + $(shell ../tools/c-conf libfunction sranddev HAVE_SRANDDEV) LIBS = $(shell ../tools/c-conf lib ucb nsl pthread socket 2>/dev/null) diff --git a/src/allocreporter.c b/src/allocreporter.c @@ -4,17 +4,6 @@ void alloc_reporter (Service *s, int first) { int shmid; struct shmid_ds shmbuf; - if (flag_foreground) { - /* Fake it! */ - msg ("Obtaining reporting memory for service %s " - "(in memory segment, NOT in shared memory " - "since we're in debug mode)", - s->name); - servicereport = xmalloc (sizeof(Servicereport)); - memset (servicereport, 0, sizeof(Servicereport)); - return; - } - /* Get an ID to work on, depending on the create mode. Get a corresponding semaphore too. */ diff --git a/src/backendavailable.c b/src/backendavailable.c @@ -19,7 +19,8 @@ int backend_available () { (activeservice->backend[i].maxconnections == 0 || activeservice->backend[i].maxconnections > servicereport->backendstate[i].nclients)) - msg ("Found (at least) one available back end"); + msg ("Service %s: found (at least) one available back end", + activeservice->name); return (1); } diff --git a/src/choosebackend.c b/src/choosebackend.c @@ -6,7 +6,9 @@ void choose_backend () { tot_weights, lo_val, hi_val, done; unsigned long long values[MAX_BACKEND], nbest; unsigned nclients; +# ifndef HAVE_SRANDDEV struct timeval tv; +# endif /* Check that we're allowed to accept at all. */ if (activeservice->maxconnections && @@ -77,9 +79,15 @@ void choose_backend () { case ds_random: /* Re-randomize. */ +# ifdef HAVE_SRANDDEV + sranddev(); + msg ("Randomier seeded with randdev"); +# else gettimeofday (&tv, 0); srand ((unsigned) tv.tv_usec); - + msg ("Randomizer seeded with %u", (unsigned) tv.tv_usec); +# endif + /* First of all let's see if all the weights are the same. */ tot_weights = 0; for (i = 0; i < nbackends; i++) { @@ -144,21 +152,24 @@ void choose_backend () { k++; } - /* Debugging: */ - for (k = 0; k < tot_weights; k++) - msg ("random: sel_weight[%d] = %d", k, sel_weights[k]); + /* Debugging: + * for (k = 0; k < tot_weights; k++) + * msg ("random: sel_weight[%d] = %d", k, sel_weights[k]); + */ - /* Select the next random back end. */ - /* + /* Select the next random back end. Algorithm is: * i = rand() % tot_weights; * j = sel_weights[i]; * k = backends[j]; * current_backend = k; - * msg ("i=%d j=%d, k=%d", i, j, k); */ - current_backend = backends[sel_weights[rand() % tot_weights]]; + * msg ("i=%d j=%d, k=%d", i, j, k); + */ + i = rand() % tot_weights; + current_backend = backends[sel_weights[i]]; servicereport->last_backend = current_backend; free (sel_weights); - msg ("Chosen backend (weighted random): %d", current_backend); + msg ("Chosen backend (weighted random): %d at index %d", + current_backend, i); return; } diff --git a/src/copysockets.c b/src/copysockets.c @@ -21,7 +21,7 @@ void copysockets (int clientsock, int serversock, struct sockaddr_in *client) { /* Cpio back and forth. */ log_activity_start (client); - + while (1) { FD_ZERO (&readset); FD_SET (clientsock, &readset); @@ -40,7 +40,7 @@ void copysockets (int clientsock, int serversock, struct sockaddr_in *client) { /* Wait for anything to happen */ nfd = select (FD_SETSIZE, &readset, 0, &exceptset, tvp); - + /* Update the intermediate activity as far as the elapsed time * is concerned. */ gettimeofday (&tv, 0); @@ -58,23 +58,20 @@ void copysockets (int clientsock, int serversock, struct sockaddr_in *client) { servicereport->backendstate[current_backend].nclients--; unlock_reporter(); log_activity_end (client); - + if (! nfd) { /* This must be caused by a timeout in select(), otherwise * the read() below would have returned 0. */ - warning ("Timout in service %s (backend %s) after %d secs", - activeservice->name, - activeservice->backend[current_backend].name, - activeservice->connectiontimeout); mark_activity (0, 0, st_available); - error ("Timout in service %s (backend %s) after %d secs", + error ("Service %s: Timeout (backend %s) after %d secs", activeservice->name, activeservice->backend[current_backend].name, activeservice->connectiontimeout); } else { /* nfd < 0: This is an error condition. * select() has failed! */ - error ("Failed to wait for network input: %s", + error ("Service %s: Failed to wait for network input: %s", + activeservice->name, strerror(errno)); } } @@ -118,20 +115,15 @@ void copysockets (int clientsock, int serversock, struct sockaddr_in *client) { lock_reporter(); servicereport->nclients--; servicereport->backendstate[current_backend].nclients--; - unlock_reporter(); + unlock_reporter(); mark_activity (0, 0, st_available); close (serversock); close (clientsock); log_activity_end (client); - /* In forked mode, we're done. In foreground mode, RTC. */ - if (flag_foreground) { - msg ("Successful TCP stop, returning to caller"); - return; - } else { - msg ("Successful TCP stop, exiting servicer %d", getpid()); - exit (0); - } + /* We're done. */ + msg ("Successful TCP stop, exiting servicer %d", getpid()); + exit (0); } else if (nread < 0) { if (src_fd == serversock) mark_activity (0, 0, st_unavailable); @@ -142,7 +134,7 @@ void copysockets (int clientsock, int serversock, struct sockaddr_in *client) { log_activity_end (client); error ("Read error on connection from %s", src_str); } - + /* Update the intermediate activity as far as the read bytes * are concerned. Update traffic / thruput logs. */ // msg ("Read %d bytes from %s", nread, src_str); diff --git a/src/crossroads.h b/src/crossroads.h @@ -183,11 +183,11 @@ EXTERN Service *activeservice; /* target service of a daemon */ EXTERN char *config_file; /* config to parse */ EXTERN int current_backend; /* of a given service */ EXTERN int daemonized; /* are we forked off yet */ -EXTERN int flag_foreground; /* flag: don't daemonize */ EXTERN int flag_verbose; /* flag: verbosity */ EXTERN int iflag_present; /* flag: -i present */ EXTERN int interrupted; /* got a signal? */ EXTERN char *laststring; /* semantic lexer value */ +EXTERN int listen_sock; /* servicer listening socket */ EXTERN int log_activity; /* log activy to syslog? */ EXTERN int log_facility; /* openlog(3) facility, -l flag */ EXTERN int logstarted; /* was syslog() called yet? */ @@ -200,7 +200,6 @@ EXTERN int semid; /* semaphore ID */ EXTERN Service *service; /* service descriptions */ EXTERN Servicereport *servicereport; /* reporter in shared mem */ EXTERN char *state_to_string_map[]; /* backend states as strings */ -EXTERN int uid; /* UID to assume */ EXTERN FILE *yyin; /* config file handle */ EXTERN int yylineno; /* input line number */ EXTERN char *yyerrmsg; /* parsing error msg */ diff --git a/src/deallocreporter.c b/src/deallocreporter.c @@ -10,24 +10,25 @@ void dealloc_reporter (Service *s) { * then ignore the error. The true handler will have already released it. */ if ( (shmid = shmget (s->shmkey, sizeof(Servicereport), - 0644) ) >= 0) + 0644) ) >= 0) { msg ("Got existing shm at id %d", shmid); - else if (program_stage != stage_main) - error ("Cannot get shared memory for service %s (key %d: %s)", - s->name, s->shmkey, strerror(errno)); - else - return; - - /* Mark to be released. If this fails and if we're stage 0, - * then ignore. - */ - if (shmctl (shmid, IPC_RMID, 0) < 0 && program_stage != stage_main) - error ("Cannot mark shared memory as destructable: %s " - "NOTE: Root will need to 'ipcrm -m %d' and 'ipcrm -s %d' " - "to clean up!", - strerror(errno), shmid, semid); - if (semctl (semid, 0, IPC_RMID) < 0 && program_stage != stage_main) - error ("Cannot remove semaphore: %s " - "NOTE: Root will need to 'ipcrm -s %d' to clean up!", - semid); + /* Mark to be released. If this fails then warn. */ + if (shmctl (shmid, IPC_RMID, 0) < 0 && program_stage != stage_main) + warning ("WARNING: Cannot mark shared memory as destructable: %s " + "Root might need to 'ipcrm -m %d' and " + "'ipcrm -s %d' to clean up!", + strerror(errno), shmid, semid); + if (semctl (semid, 0, IPC_RMID) < 0 && program_stage != stage_main) + warning ("WARNING: Cannot remove semaphore: %s " + "Root might need to 'ipcrm -s %d' to clean up!", + strerror(errno), semid); + } + else if (program_stage != stage_main) { + /* Errno 2 means that the shm block was already removed (by a + * different listener) Don't warn if that's the case. + */ + if (errno != 2) + warning ("Cannot get shared memory for service %s (key %d: %d/%s)", + s->name, s->shmkey, errno, strerror(errno)); + } } diff --git a/src/forktcpservicer.c b/src/forktcpservicer.c @@ -3,9 +3,6 @@ int fork_tcp_servicer (int to_backend) { int pid, i; - if (flag_foreground) - return (0); - if ( (pid = fork()) < 0 ) /* Fork failure */ error ("Failed to fork: %s", strerror(errno)); diff --git a/src/httpread.c b/src/httpread.c @@ -70,8 +70,6 @@ void http_read (int sock, int isclient, unsigned char **bufp, int *buflen) { if (!isclient) servicereport->backendstate[current_backend].nclients--; unlock_reporter(); - if (!isclient) - mark_activity (0, 0, st_available); if (nread < 0) warning ("Failed to read HTTP input: %s", strerror(errno)); else @@ -109,8 +107,6 @@ void http_read (int sock, int isclient, unsigned char **bufp, int *buflen) { if (!isclient) servicereport->backendstate[current_backend].nclients--; unlock_reporter(); - if (!isclient) - mark_activity (0, 0, st_available); msg ("HTTP read: complete message (%d bytes) '%s'", *buflen, *bufp); return; diff --git a/src/httpserve.c b/src/httpserve.c @@ -20,7 +20,7 @@ void http_serve (int clientsock, struct sockaddr_in *client) { */ if (is_continuation) log_activity_continuation (client); - + if (http_write (serversock, firstbuf, buflen)) { mark_activity (0, 0, st_unavailable); error ("Failed to send data to backend '%s'", @@ -37,17 +37,19 @@ void http_serve (int clientsock, struct sockaddr_in *client) { http_set_sticky_cookie (&secondbuf, &buflen); if (http_write (clientsock, secondbuf, buflen)) error ("Failed to send data to client"); - free (secondbuf); } - /* Now we have a client and server socket, possibly with a - * session cookie in the chat. Piggyback to & fro. */ - copysockets (clientsock, serversock, client); - + /* Now we might have to piggyback to and fro -- tho only if the + * HTTP chat wasn't done yet. */ + if (http_complete (secondbuf, buflen)) + mark_activity (0, 0, st_available); + else + copysockets (clientsock, serversock, client); + free (secondbuf); + /* Normal shutdown: TCP traffic done. */ msg ("Normal HTTP stop."); close (serversock); close (clientsock); - if (! flag_foreground) - exit (0); + exit (0); } diff --git a/src/interrupt.c b/src/interrupt.c @@ -6,24 +6,29 @@ void interrupt (int sig) { switch (program_stage) { case stage_waiting: - warning ("LISTENER: " - "Caught signal %d, freeing reporter and stopping", - sig); - dealloc_reporter (activeservice); - exit (1); + warning ("Service %s: listener caught signal %d, " + "closing socket %d and exiting", + activeservice->name, sig, listen_sock); + if (shutdown (listen_sock, SHUT_RDWR)) + error ("Service %s: can't shut down server-side socket %d: %s", + activeservice->name, listen_sock, strerror(errno)); + if (close (listen_sock)) + error ("Service %s: can't close socket %d: %s", + activeservice->name, listen_sock, strerror(errno)); + exit (0); case stage_serving: - warning ("CONNECTION SERVICER: " - "Caught signal %d, will stop after servicing connection", - sig); + warning ("Service %s: servicer caught signal %d, " + "will exit after servicing my connection", + activeservice->name, sig); break; case stage_retrying: - warning ("WAKEUP HANDER: " - " Caught signal %d, will stop waking up backend and exit", - sig); - exit (1); + warning ("Service %s: wakeup handler caught signal %d, " + "stopping wake ups and exiting", + activeservice->name, sig); + exit (0); default: - msg ("Caught signal %d in program stage %s", sig, + msg ("Caught signal %d in program stage %s, exiting", sig, stage_to_string (program_stage)); - exit (1); + exit (0); } } diff --git a/src/lockreporter.c b/src/lockreporter.c @@ -15,9 +15,6 @@ void lock_reporter() { } }; - if (flag_foreground) - return; - /* msg ("Locking reporter memory"); */ if ( (!warning_issued++) && (semop (semid, buf, 2) < 0) ) { warning ("Failed to lock reporter memory (stage %s): %s", diff --git a/src/main.c b/src/main.c @@ -52,7 +52,7 @@ int main (int argc, char **argv) { /* Parse options. */ config_file = DEFAULT_CONF; - while ( (opt = getopt (argc, argv, "?c:fhvi:u:Val:")) > 0 ) + while ( (opt = getopt (argc, argv, "?c:fhvi:Val:")) > 0 ) switch (opt) { case 'a': log_activity++; @@ -60,9 +60,6 @@ int main (int argc, char **argv) { case 'c': config_file = optarg; break; - case 'f': - flag_foreground++; - break; case 'v': flag_verbose++; break; @@ -99,12 +96,6 @@ int main (int argc, char **argv) { usage(); } break; - case 'u': - if (! (passwd = getpwnam (optarg)) ) - error ("Cannot fetch information for user '%s': %s", - optarg, strerror(errno)); - uid = passwd->pw_uid; - break; case 'V': puts (VER); exit (0); diff --git a/src/makesocket.c b/src/makesocket.c @@ -7,11 +7,13 @@ int make_socket (int port, char const *ipaddr) { /* Create the socket. */ if ( (sock = socket (PF_INET, SOCK_STREAM, 0)) < 0) { - warning ("Cannot create network socket: %s", strerror(errno)); + warning ("Service %s: cannot create network socket: %s", + activeservice->name, strerror(errno)); return (-1); } if (setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val))) { - warning ("Cannot set socket options: %s", strerror(errno)); + warning ("Service %s: cannot set socket options: %s", + activeservice->name, strerror(errno)); close (sock); return (-2); } @@ -22,20 +24,23 @@ int make_socket (int port, char const *ipaddr) { /* Propagate 'bindto' if given. */ if (strcasecmp (ipaddr, "any")) { - msg ("Creating socket for IP address '%s'", ipaddr); + msg ("Service %s: creating socket for IP address '%s'", + activeservice->name, ipaddr); if ( (name.sin_addr.s_addr = inet_addr (ipaddr)) == INADDR_NONE ) { close (sock); - error ("Cannot convert address '%s' to network bytes", - ipaddr); + error ("Service %s: cannot convert address '%s' to network bytes", + activeservice->name, ipaddr); } } else { - msg ("Creating socket for all interfaces"); + msg ("Service %s: creating socket for all interfaces", + activeservice->name); name.sin_addr.s_addr = htonl (INADDR_ANY); } /* Finallly, bind the socket. */ if (bind (sock, (struct sockaddr *) &name, sizeof (name)) < 0) { - warning ("Cannot bind socket to port %d: %s", port, strerror(errno)); + warning ("Service %s: cannot bind socket to port %d: %s", + activeservice->name, port, strerror(errno)); close (sock); return (-3); } diff --git a/src/markactivity.c b/src/markactivity.c @@ -16,8 +16,7 @@ void mark_activity (unsigned long long nbytes, double nsec, /* Handle the decays of all other back ends */ for (i = 0; i < activeservice->nbackend; i++) { - if (i != current_backend && - activeservice->backend[i].decay) { + if (i != current_backend && activeservice->backend[i].decay) { if (activeservice->dispatchover) { servicereport->backendstate[i].avg_nbytes *= (100 - activeservice->backend[i].decay); diff --git a/src/restart.c b/src/restart.c @@ -17,8 +17,9 @@ void restart (int ac, char **av) { "Crossroads may be in an unstable state, fix by hand!", cmd); - /* Wait a few secs to allow the listening socket to free up. */ - sleep (1); + /* Wait a few secs to allow the listening socket to free up. + * sleep (1); + */ /* Now run the 'start' action */ org_argv[org_argc - 1] = "start"; diff --git a/src/runservice.c b/src/runservice.c @@ -1,56 +1,51 @@ #include "crossroads.h" void runservice () { - int listen_sock, pid; + int pid; - msg ("Service: %s (on port %d)", + msg ("Service %s (on port %d): STARTING", activeservice->name, activeservice->port); /* Allocate the shared mem segment. */ alloc_reporter(activeservice, 1); - /* Now if required, go into the background. */ - if (!flag_foreground) { - - if ( (pid = fork()) < 0 ) { - /* Fork failed */ - error ("Fork failure: %s", strerror(errno)); - } else if (pid) { - /* Parent branch */ - msg ("Service %s detached as PID %d", - activeservice->name, pid); - return; - } else { - /* Child branch */ - if (uid) { - if (seteuid(uid)) - error ("Cannot assume effective uid %d: %s", - uid, strerror(errno)); - } - - set_program_title ("service %s listening", activeservice->name); - close (0); - close (1); - close (2); - daemonized++; - if ( (open ("/dev/null", O_RDONLY) < 0) || - (open ("/dev/null", O_WRONLY) < 0) || - (open ("/dev/null", O_WRONLY) < 0) ) - error ("Failed to reopen stdin/out/err on /dev/null"); - if (setsid() < 0) - error ("Failed to become seesion leader"); + /* Go into the background. */ + if ( (pid = fork()) < 0 ) { + /* Fork failed */ + error ("Service %s: fork failure: %s", + activeservice->name, strerror(errno)); + } else if (pid) { + /* Parent branch */ + msg ("Service %s: detached as PID %d", + activeservice->name, pid); + return; + } else { + /* Child branch */ + set_program_title ("service %s listening", activeservice->name); + close (0); + close (1); + close (2); + daemonized++; + if ( (open ("/dev/null", O_RDONLY) < 0) || + (open ("/dev/null", O_WRONLY) < 0) || + (open ("/dev/null", O_WRONLY) < 0) ) + error ("Service %s: " + "failed to reopen stdin/out/err on /dev/null", + activeservice->name); + if (setsid() < 0) + error ("Service %s: failed to become seesion leader", + activeservice->name); - /* Store our pid, so that 'crossroads stop' may kill us. */ - lock_reporter(); - servicereport->pid = getpid(); - unlock_reporter(); + /* Store our pid, so that 'crossroads stop' may kill us. */ + lock_reporter(); + servicereport->pid = getpid(); + unlock_reporter(); - /* Promote verbosity. */ - flag_verbose = activeservice->verbosity; - - /* We've forked for the first time */ - program_stage = stage_waiting; - } + /* Promote verbosity. */ + flag_verbose = activeservice->verbosity; + + /* We've forked for the first time */ + program_stage = stage_waiting; } /* In 'forever' mode, we create a server-side socket and ask tcpserve() @@ -64,7 +59,9 @@ void runservice () { /* Create the socket, bind to port. */ if ( (listen_sock = make_socket (activeservice->port, activeservice->bind)) < 0 ) { - warning ("Listening socket creation failed.. will retry"); + warning ("Service %s: " + "listening socket creation failed.. will retry", + activeservice->name); /* We wait here for some time, to let the system regain * equilibrium. Something's really afoot, so let's not retry @@ -73,7 +70,8 @@ void runservice () { continue; } - msg ("Server-side network socket: %d", listen_sock); + msg ("Service %s: server-side network socket: %d", + activeservice->name, listen_sock); /* Start serving the port! */ tcpserve (listen_sock); @@ -81,15 +79,16 @@ void runservice () { /* tcpserve() returned -- must be because this service is out * of back ends */ if (shutdown (listen_sock, SHUT_RDWR)) - error ("Failed to shut down server-side socket %d: %s", - listen_sock, strerror(errno)); + error ("Service %s: " + "failed to shut down server-side socket %d: %s", + activeservice->name, listen_sock, strerror(errno)); if (close (listen_sock)) - error ("Failed to close server-side socket %d: %s", - listen_sock, strerror(errno)); + error ("Service %s: failed to close server-side socket %d: %s", + activeservice->name, listen_sock, strerror(errno)); } - msg ("Taking a nap..."); + msg ("Service %s: taking a nap...", activeservice->name); sleep (SLEEP_TIME); } } diff --git a/src/sample.conf b/src/sample.conf @@ -10,7 +10,7 @@ * over a couple of webserver back ends. */ service www { - + /* Port on which we'll listen in this service: required. */ port 8000; @@ -18,15 +18,15 @@ service www { * Alternatively you can state an explicit IP address, such as * 127.0.0.1; that would bind the service only to 'localhost'. */ bindto any; - - /* Verbose reporting or not. Default is off. */ + + /* Verbose reporting or not. Default is off. */ verbosity on; /* Service type: 'http' or 'any'. With 'http' you can make sessions * "stick" to a once selected back end. If you don't need stickiness, * use 'any' which is the default. */ type any; - + /* Dispatching mode, or: How to select a back end for an incoming * request. Possible values: * roundrobin: just the next back end in line @@ -40,8 +40,8 @@ service www { * byduration: The backend that was active for the shortest time * is the next in line. As a modifier you can say e.g. * byduration of 10 to compute over the last 10 connections. - * byconnections: The back end with the least number of active - * connections is the next in line. + * byconnections: The back end with the least number of active + * connections is the next in line. * byorder: The first available back end is always taken. */ dispatchmode byduration over 5; @@ -50,12 +50,12 @@ service www { * backend has woken up. */ revivinginterval 5; - + /* TCP backlog of connections. Default is 0 (no backlog, one * connection may be active). */ backlog 5; - + /* For status reporting: a shared memory key. Default is the same * as the port number, OR-ed by a magic number. */ @@ -89,7 +89,7 @@ service www { * end. */ decay 10; - + /* To see what's happening in /var/log/messages: */ verbosity on; } @@ -106,8 +106,8 @@ service www { /* Decay */ decay 10; - /* Performance related */ - throughputlog /tmp/backend.2.perflog; + /* Performance related */ + throughputlog /tmp/backend.2.perflog; /* Event triggers for system commands upon succesful activation * and upon failure. @@ -126,27 +126,27 @@ service www { verbosity on; decay 10; trafficlog /tmp/backend.3.log; - maxconnections 10; + maxconnections 10; } } /* Another example: SSH login failover. */ service ssh { port 2222; // Incoming port - type any; // Generic TCP service + type any; // Generic TCP service verbosity on; // Let's be verbose shmkey 2222; // Shared memory related connectiontimeout 30; // Auto-logout after 30 secs dispatchmode bysize over 1; // Least bytes = least active, // only last connection matters maxconnections 1; // 1 concurrent login - + backend ssh_1 { // First back end server sshserver1:22; verbosity on; onfailure echo 'SSH failure on sshserver1' | mail root; } - + backend ssh_2 { // Second back end server sshserver2:22; verbosity on; @@ -162,23 +162,22 @@ service balancedapp { port 8001; type stickyhttp; backend app1 { - server appserver1:8080; - /* If 'BalancerID=a' is already in the browser's request, then - * the request automatically goes to this backend */ - stickycookie BalancerID=a; - /* The server return is enriched with the following cookie - * instruction (note that this matches 'stickycookie' above) */ - insertcookie "BalancerID=a; Path=/"; + server appserver1:8080; + /* If 'BalancerID=a' is already in the browser's request, then + * the request automatically goes to this backend */ + stickycookie BalancerID=a; + /* The server return is enriched with the following cookie + * instruction (note that this matches 'stickycookie' above) */ + insertcookie "BalancerID=a; Path=/"; } backend app2 { - server appserver2:8080; - stickycookie BalancerID=b; - insertcookie "BalancerID=b; Path=/"; + server appserver2:8080; + stickycookie BalancerID=b; + insertcookie "BalancerID=b; Path=/"; } backend app3 { - server appserver3:8080; - stickycookie BalancerID=c; - insertcookie "BalancerID=c; Path=/"; + server appserver3:8080; + stickycookie BalancerID=c; + insertcookie "BalancerID=c; Path=/"; } } - diff --git a/src/serve.c b/src/serve.c @@ -12,7 +12,7 @@ void serve (int ac, char **av) { * space on the commanedline. */ # if SET_PROC_TITLE_BY_ARGV == 1 - if (! flag_foreground && ! iflag_present) { + if (! iflag_present) { char **argv = xmalloc ( (org_argc + 2) * sizeof(char*)); int i; @@ -33,10 +33,6 @@ void serve (int ac, char **av) { # endif for (i = 0; i < nservice; i++) { - /* With -f flag, don't run second and next services. */ - if (i && flag_foreground) - break; - /* Start the service. */ activeservice = service + i; runservice (); diff --git a/src/tcpserve.c b/src/tcpserve.c @@ -7,7 +7,7 @@ void tcpserve (int server_sock) { static int wakeup_started = 0; /* Set up our signal handlers. */ - if (!flag_foreground && !wakeup_started++) { + if (!wakeup_started++) { /* Interruption signals */ for (i = 0; relevant_sigs[i]; i++) signal (relevant_sigs[i], interrupt); @@ -19,17 +19,19 @@ void tcpserve (int server_sock) { set_program_title ("wakeup for %s", activeservice->name); wakeup_handler(); } else { - msg ("Started wakeup handler at pid %d", pid); + msg ("Service %s: started wakeup handler at pid %d", + activeservice->name, pid); servicereport->rev_pid = pid; } } } - msg ("Awaiting activity on port %d (socket fd %d)", - activeservice->port, server_sock); + msg ("Service %s: awaiting activity on port %d (socket fd %d)", + activeservice->name, activeservice->port, server_sock); if (listen (server_sock, activeservice->backlog + 1) < 0) - error ("Failed to listen to server_socket: %s", strerror(errno)); + error ("Service %s: failed to listen to server_socket: %s", + activeservice->name, strerror(errno)); /* Avoid zombies. */ signal (SIGCHLD, SIG_IGN); @@ -42,15 +44,15 @@ void tcpserve (int server_sock) { FD_ZERO (&set); FD_SET (server_sock, &set); if (select (FD_SETSIZE, &set, 0, 0, 0) < 0) - error ("Error while waiting for activity: %d (%s)", - errno, strerror(errno)); + error ("Service %s: error while waiting for activity: %d (%s)", + activeservice->name, errno, strerror(errno)); /* Accept the client-side connection. */ size = sizeof(clientname); if ( (new = accept (server_sock, (struct sockaddr *) &clientname, (socklen_t *) &size)) < 0 ) - error ("Failure while accepting on server socket: %s", - strerror(errno)); + error ("Service %s: failure while accepting on server socket: %s", + activeservice->name, strerror(errno)); msg ("Connect on service %s from %s", activeservice->name, @@ -70,7 +72,8 @@ void tcpserve (int server_sock) { * create a new one. */ if (! backend_available()) { - warning ("No back ends available"); + warning ("Service %s: no back ends available", + activeservice->name); close (new); return; } @@ -86,7 +89,8 @@ void tcpserve (int server_sock) { } /* Show what's happening. */ - msg ("Trying back end %s, port %d", + msg ("Service %s: trying back end %s, port %d", + activeservice->name, activeservice->backend[current_backend].server, activeservice->backend[current_backend].port); @@ -95,7 +99,8 @@ void tcpserve (int server_sock) { * In the loop we may need to decide to return after all. */ if ( (backend_sock = backend_connect(0)) < 0 ) { sysrun (activeservice->backend[current_backend].onfailure); - warning ("Failed to connect to server %s:%d", + warning ("Service %s: failed to connect to server %s:%d", + activeservice->name, activeservice->backend[current_backend].server, activeservice->backend[current_backend].port); continue; diff --git a/src/unlockreporter.c b/src/unlockreporter.c @@ -9,9 +9,6 @@ void unlock_reporter() { 0, /* no special flags */ }; - if (flag_foreground) - return; - /* msg ("UnLocking reporter memory"); */ if ( (! warning_issued++) && (semop (semid, &buf, 1) < 0) ) { warning ("Failed to unlock reporter memory (stage %s): %s", diff --git a/src/usage.txt b/src/usage.txt @@ -22,21 +22,10 @@ Supported flags: -a: logs starting and finishing activity to syslog -c CONFIG: Uses the named configuration, instead of the default %s - -f: With '(re)start', causes crossroads to stay in the foreground - instead of daemonizing. Useful for debugging with -v. No - 'status' reporting is possible, no wakeup handlers will run. -l FAC: Specifies the openlog(3) facility to use when - logging. Default is LOG_DAEMON. Allowed values are 0..7 - for LOG_LOCAL0 to LOG_LOCAL7. - -u USER: With '(re)start', causes the daemons to run under the - permissions of the stated user. Unused when combined with -f. - -v: Enables verbosity upon startup and, when used with -f, in the - services. Normally the services verbosity is controlled via - 'verbosity' statements in the configuration. - -V: Shows the version ID and stops. + logging. Default is LOG_DAEMON. Allowed values are 0..7 + for LOG_LOCAL0 to LOG_LOCAL7. + -v: Enables verbosity upon startup. Other verbosity (services + and back ends) is controlled in the configuration. + -V: Shows the version ID and stops. -?, -h: Shows this message. - -NOTE: Stopping, restarting and status reporting depends on the -configuration that was in use when crossroads was started. Therefore, -before adding or removing services in the configuration, 'stop' the -running services first. diff --git a/src/xmalloc.c b/src/xmalloc.c @@ -1,9 +1,5 @@ #include "crossroads.h" void *xmalloc (unsigned sz) { - void *ret; - - if (! (ret = malloc (sz)) ) - error ("Out of memory (while allocating %u bytes)", sz); - return (ret); + return (xrealloc (0, sz)); } diff --git a/src/xrealloc.c b/src/xrealloc.c @@ -1,12 +1,22 @@ #include "crossroads.h" -void *xrealloc (void *what, unsigned sz) { +void *xrealloc (void *block, unsigned sz) { + void *newblock; + + // msg ("Realloc: block=%x, sz=%u", block, sz); if (! sz) { - free (what); - return (0); + free (block); + newblock = 0; + // msg ("Realloc: freed %x", block); + } else if (!block) { + if (! (newblock = malloc (sz)) ) + error ("Out of memory (while allocating %u bytes)", sz); + // msg ("Realloc: malloc'd %x (%u bytes)", newblock, sz); + } else { + if (! (newblock = realloc (block, sz)) ) + error ("Out of memory (while increasing block to %u bytes)", sz); + // msg ("Realloc'd %x to %x (%u bytes)", block, newblock, sz); } - if (! (what = realloc (what, sz)) ) - error ("Out of memory (while increasing block to %u bytes)", sz); - return (what); + return (newblock); } diff --git a/test/client b/test/client @@ -0,0 +1,56 @@ +#!/usr/bin/perl + +use strict; +use Socket; + +if ($#ARGV != 1) { + die <<"ENDUSAGE"; + +Usage: client host port + +ENDUSAGE +} + +my ($host, $port) = @ARGV; + +print ("Starting client loop to query $host:$port.. press ^C to stop\n"); +sleep (1); + +my %hits = (a => 0, b => 0, c => 0, unknown => 0, errors => 0); + +while (1) { + open (my $if, "wget -vSO- http://$host:$port 2>&1 |") + or die ("Can't start wget: $!\n"); + my ($a, $b, $c, $unk, $err); + $err = 1; + while (my $msg = <$if>) { + chomp ($msg); + # print ("Server says: [$msg]\n"); + if ($msg =~ /HTTP.*200 OK/ or $msg =~ /Hello World/) { + $err = 0; + } elsif ($msg =~ /Backend_A/) { + $a = 1; + } elsif ($msg =~ /Backend_B/) { + $b = 1; + } elsif ($msg =~ /Backend_C/) { + $c = 1; + } + } + close ($if); + + $unk = 1 if ($a == 0 and $b == 0 and $c == 0); + + $hits{a} += $a; + $hits{b} += $b; + $hits{c} += $c; + $hits{unknown} += $unk; + $hits{errors} += $err; + + for my $k (sort (keys (%hits))) { + print ("$k=$hits{$k} "); + } + print ("\n"); + + # sleep (1); + select (undef, undef, undef, 0.2); +}; diff --git a/test/server b/test/server @@ -0,0 +1,94 @@ +#!/usr/bin/perl + +use strict; +use Socket; +use POSIX; + +# Verbose messaging +sub msg { + print STDERR ("server: ", @_); +} + +# Child process catcher +sub reaper { + while ((my $waitedpid = waitpid(-1,WNOHANG)) > 0) { + msg ("Child $waitedpid done", + ($? ? " with exit $?" : ''), + "\n"); + } + $SIG{CHLD} = \&reaper; +} + +# Connection handler +sub handleconnection () { + my $pid; + + # Daemonize + if (!defined ($pid = fork())) { + msg ("Can't fork: $!\n"); + return; + } elsif ($pid) { + # Parent branch + msg ("Connection handler started as pid $pid\n"); + return; + } + + # Child branch + select Client; + $| = 1; + + my $msg = "Hello World!\n"; + print ("HTTP/1.0 200 OK\r\n", + "Content-length: ", length($msg), "\r\n", + "\r\n", + $msg); + exit (0); +} + +# Daemon server +sub serve ($) { + my $port = shift; + + # Create the tcp service. + socket (Server, PF_INET, SOCK_STREAM, getprotobyname ('tcp')) + or die ("Can't create server socket: $!\n"); + setsockopt (Server, SOL_SOCKET, SO_REUSEADDR, pack("l", 1)) + or die ("Can't set socket options: $!\n"); + bind (Server, sockaddr_in ($port, INADDR_ANY)) + or die ("Can't bind to port: $!\n"); + listen (Server, SOMAXCONN) + or die ("Can't listen to socket: $!\n"); + msg ("Server started on port $port.\n"); + + $SIG{CHLD} = \&reaper; + $SIG{INT} = sub { + msg ("Interrupt caught, terminating..\n"); + exit(0); + }; + + while (1) { + for (my $waitedpid = 0; + (my $paddr = accept (Client, Server)) || $waitedpid; + $waitedpid = 0, close (Client)) { + next if ($waitedpid and not $paddr); + my ($port, $iaddr) = sockaddr_in ($paddr); + my $name = gethostbyaddr ($iaddr, AF_INET); + + msg ("Connection from $name/", inet_ntoa ($iaddr), ":$port\n"); + + handleconnection (); + } + } +} + +# Main starts here +if ($#ARGV != 0) { + die <<"ENDUSAGE"; + +Usage: server portnr + +ENDUSAGE +} + +serve (shift (@ARGV)); + diff --git a/test/t1.conf b/test/t1.conf @@ -0,0 +1,36 @@ +service test { + port 10001; + type stickyhttp; + bindto any; + revivinginterval 5; + dispatchmode bysize; + connectiontimeout 600; + verbosity on; + + backend a { + server localhost; + port 10000; + weight 2; + decay 5; + stickycookie BalancerID=Backend_A; + insertcookie "BalancerID=Backend_A; Path=/"; + } + + backend b { + server localhost; + port 10000; + weight 2; + decay 5; + stickycookie BalancerID=Backend_B; + insertcookie "BalancerID=Backend_B; Path=/"; + } + + backend c { + server localhost; + port 10000; + weight 3; + decay 5; + stickycookie BalancerID=Backend_C; + insertcookie "BalancerID=Backend_C; Path=/"; + } +} diff --git a/test/t2.conf b/test/t2.conf @@ -0,0 +1,36 @@ +service test { + port 10001; + type stickyhttp; + bindto any; + revivinginterval 5; + dispatchmode random; + connectiontimeout 600; + verbosity on; + + backend a { + server localhost; + verbosity on; + port 10000; + weight 2; + stickycookie BalancerID=Backend_A; + insertcookie "BalancerID=Backend_A; Path=/"; + } + + backend b { + server localhost; + verbosity on; + port 10000; + weight 3; + stickycookie BalancerID=Backend_B; + insertcookie "BalancerID=Backend_B; Path=/"; + } + + backend c { + server localhost; + verbosity on; + port 10000; + weight 3; + stickycookie BalancerID=Backend_C; + insertcookie "BalancerID=Backend_C; Path=/"; + } +} diff --git a/test/t3.conf b/test/t3.conf @@ -0,0 +1,13 @@ +/* Another testing scenario. + * This one forwards port 23 to localhost:22 (sshd), and logs out + * after 1 minute of inactivity. */ + +service ssh { + port 23; + connectiontimeout 60; + verbosity true; + backend a { + verbosity true; + server localhost:22; + } +} diff --git a/tools/c-conf b/tools/c-conf @@ -4,7 +4,10 @@ use strict; use Getopt::Std; # Globals -my $VER = "1.01"; +my $VER = "1.03"; +# 1.03 [KK 2006-07-19] 'subfiles' keeps track of visited dirs incase of +# recursion. Testing is now by inode, used to be by name. +# 1.02 [KK 2006-06-01] 'findbin' searches for .exe too now, for Cygwin support # 1.01 [KK 2005-09-29] Implemented context-sensitive help via -h. # Action 'header' implemented. # 1.00 [KK 2005-09-28] First version @@ -111,11 +114,11 @@ sub uname() { } # Find a binary along the path. -sub findbin ($) { +sub findbin($) { my $bin = shift; msg ("Looking for executable '$bin'\n"); foreach my $d (split (/:/, $ENV{PATH})) { - if (-x "$d/$bin") { + if (-x "$d/$bin" or -f "$d/bin.exe") { msg ("Found as '$d/$bin'\n"); return ("$d/$bin"); } @@ -129,12 +132,14 @@ sub subfiles ($$$) { my ($dir, $mask, $recursive) = @_; %_dir_visited = () unless ($recursive); - if ($_dir_visited{$dir}) { - msg ("Path '$dir' was already visited\n"); + my ($dev, $ino) = stat($dir) + or return (undef); + my $tag = sprintf ("%d-%d", $dev, $ino); + if ($_dir_visited{$tag}) { + msg ("Path '$dir' was already visited (as $_dir_visited{$tag})\n"); return (undef); } - my ($dev, $ino) = stat($dir); - $_dir_visited{$dir} = sprintf ("%d-%d", $dev, $ino); + $_dir_visited{$tag} = $dir; msg ("Scanning for '$mask' under '$dir'\n"); if (! -d $dir) { @@ -185,7 +190,7 @@ sub if_header { When found, a define-flag for the C compiler is returned. E.g.: $base ifheader malloc.h HAVE_MALLOC_H (may return -DHAVE_MALLOC_H) Use in a Makefile as in: -CFLAGS = $(CFLAGS) $(shell c-conf ifheader malloc.h HAVE_MALLOC_H +CFLAGS = \$(CFLAGS) \$(shell c-conf ifheader malloc.h HAVE_MALLOC_H Then in a C source as: #ifdef HAVE_MALLOC_H #include <malloc.h> @@ -298,13 +303,13 @@ sub so_cflags { building objects for a shared library. E.g.: $base so-cflags (may return '-fPIC') Use in a Makefile as in: -CFLAGS = -c -g -Wall $(shell c-conf so-cflags) +CFLAGS = -c -g -Wall \$(shell c-conf so-cflags) ENDHELP usage() if ($#_ > -1); if (uname() eq 'Darwin') { output ('-fPIC'); - } else { + } elsif (uname() eq 'Linux') { output ('-fpic'); } } @@ -316,7 +321,7 @@ sub so_lflags { combining objects into a shared library. E.g.: $base so-lflags (may return '-dynamiclib -Wl,-single_module') Use in a Makefile as in: -MY_SO = \$(shell c-conf s-name my) +MY_SO = \$(shell c-conf so-name my) \$(MY_SO): *.o \$(CC) -o \$(MY_SO) \$(shell c-conf so-lflags) *.o ENDHELP