winlin

remove the unused files. add upp project file

要显示太多修改。

为保证性能只显示 19 of 19+ 个文件。

1 -WELCOME!  
2 -  
3 -The State Threads Library is a small application library which provides  
4 -a foundation for writing fast and highly scalable Internet applications  
5 -(such as web servers, proxy servers, mail transfer agents, and so on,  
6 -really any network-data-driven application) on UNIX-like platforms. It  
7 -combines the simplicity of the multithreaded programming paradigm, in  
8 -which one thread supports each simultaneous connection, with the  
9 -performance and scalability of an event-driven state machine  
10 -architecture. In other words, this library offers a threading API for  
11 -structuring an Internet application as a state machine. For more  
12 -details, please see the library documentation in the "docs" directory or  
13 -on-line at  
14 -  
15 - http://state-threads.sourceforge.net/docs/  
16 -  
17 -The State Threads Project is an open source project for maintaining and  
18 -enhancing the State Threads Library. For more information about this  
19 -project, please see  
20 -  
21 - http://state-threads.sourceforge.net/  
22 -  
23 -  
24 -BUILDING  
25 -  
26 -To build the library by hand, use the GNU make utility. Run the make  
27 -command (e.g., `gmake') with no arguments to display all supported  
28 -targets.  
29 -  
30 -To build more or less automatically, first set the CONFIG_GUESS_PATH  
31 -variable in either osguess.sh or your environment then run "make  
32 -default" which guesses your OS and builds. Requires the "config.guess"  
33 -utility from GNU autoconf (not included with ST). You can use one from  
34 -a larger "main" software project or just use any config.guess available  
35 -on your system. You can also get it directly from GNU:  
36 -ftp://ftp.gnu.org/gnu/autoconf/  
37 -  
38 -To build rpms (RedHat Linux 6.2 or later, Linux/Mandrake, Solaris with  
39 -gnome, etc.):  
40 - download the latest st-x.y.tar.gz  
41 - # rpm -ta st-x.y.tar.gz  
42 -The .rpms will land in /usr/src/RPMS/<arch>. Install them with:  
43 - # rpm -i libst*.rpm  
44 -Requires GNU automake and rpm 3.0.3 or later.  
45 -  
46 -Debian users:  
47 - If you run potato, please upgrade to woody.  
48 - If you run woody, "apt-get install libst-dev" will get you v1.3.  
49 - If you run testing/unstable, you will get the newest available version.  
50 - If you *must* have the newest libst in woody, you may follow these  
51 - not-recommended instructions:  
52 - 1. Add "deb-src <your-favourite-debian-mirror> unstable main" to your  
53 - /etc/apt/sources.list  
54 - 2. apt-get update  
55 - 3. apt-get source st  
56 - 4. cd st-1.4 (or whatever version you got)  
57 - 5. debuild  
58 - 6. dpkg -i ../*.deb  
59 -  
60 -If your application uses autoconf to search for dependencies and you  
61 -want to search for a given version of libst, you can simply add  
62 - PKG_CHECK_MODULES(MYAPP, st >= 1.3 mumble >= 0.2.23)  
63 -to your configure.ac/in. This will define @MYAPP_LIBS@ and  
64 -@MYAPP_CFLAGS@ which you may then use in your Makefile.am/in files to  
65 -link against mumble and st.  
66 -  
67 -  
68 -LICENSE  
69 -  
70 -The State Threads library is a derivative of the Netscape Portable  
71 -Runtime library (NSPR). All source code in this directory is  
72 -distributed under the terms of the Mozilla Public License (MPL) version  
73 -1.1 or the GNU General Public License (GPL) version 2 or later. For  
74 -more information about these licenses please see  
75 -http://www.mozilla.org/MPL/ and http://www.gnu.org/copyleft/.  
76 -  
77 -All source code in the "examples" directory is distributed under the BSD  
78 -style license.  
79 -  
80 -  
81 -PLATFORMS  
82 -  
83 -Please see the "docs/notes.html" file for the list of currently  
84 -supported platforms.  
85 -  
86 -  
87 -DEBUGGER SUPPORT  
88 -  
89 -It's almost impossible to print SP and PC in a portable way. The only  
90 -way to see thread's stack platform-independently is to actually jump to  
91 -the saved context. That's what the _st_iterate_threads() function does.  
92 -Do the following to iterate over all threads:  
93 -  
94 -- set the _st_iterate_threads_flag to 1 in debugger  
95 -- set breakpoint at the _st_show_thread_stack() function  
96 - (which does nothing)  
97 -- call the _st_iterate_threads() function which jumps to the  
98 - next thread  
99 -- at each break you can explore thread's stack  
100 -- continue  
101 -- when iteration is complete, you return to the original  
102 - point (you can see thread id and a message as arguments of  
103 - the _st_show_thread_stack() function).  
104 -  
105 -You can call _st_iterate_threads() in three ways:  
106 -  
107 -- Insert it into your source code at the point you want to  
108 - go over threads.  
109 -- Just run application and this function will be called at  
110 - the first context switch.  
111 -- Call it directly from the debugger at any point.  
112 -  
113 -This works with gdb and dbx.  
114 -  
115 -Example using gdb:  
116 -  
117 -(gdb) set _st_iterate_threads_flag = 1  
118 -(gdb) b _st_show_thread_stack  
119 -...  
120 -(gdb) call _st_iterate_threads()  
121 -...  
122 -(gdb) bt  
123 -...  
124 -(gdb) c  
125 -...  
126 -(gdb) bt  
127 -...  
128 -(gdb) c  
129 -...  
130 -and so on...  
131 -  
132 -_st_iterate_threads_flag will be set to 0 automatically  
133 -after iteration is over or you can set it to 0 at any time  
134 -to stop iteration.  
135 -  
136 -Sometimes gdb complains about SIGSEGV when you call a function  
137 -directly at gdb command-line. It can be ignored -- just call the  
138 -same function right away again, it works just fine. For example:  
139 -  
140 -(gdb) set _st_iterate_threads_flag = 1  
141 -(gdb) b _st_show_thread_stack  
142 -Breakpoint 1 at 0x809bbbb: file sched.c, line 856.  
143 -(gdb) call _st_iterate_threads()  
144 -Program received signal SIGSEGV, Segmentation fault.  
145 -....  
146 -(gdb) # just call the function again:  
147 -(gdb) call _st_iterate_threads()  
148 -Breakpoint 1, _st_show_thread_stack (thread=0x4017aee4, messg=0x80ae7a2  
149 -"Iteration started") at sched.c:856  
150 -856 }  
151 -....  
152 -  
153 -You can use simple gdb command-line scripting to display  
154 -all threads and their stack traces at once:  
155 -  
156 -(gdb) while _st_iterate_threads_flag  
157 - >bt  
158 - >c  
159 - >end  
160 -....  
161 -  
162 -Another script to stop at the thread with the specific thread id  
163 -(e.g., 0x40252ee4):  
164 -  
165 -(gdb) # set the flag again:  
166 -(gdb) set _st_iterate_threads_flag = 1  
167 -(gdb) call _st_iterate_threads()  
168 -Breakpoint 1, _st_show_thread_stack (thread=0x4017aee4, messg=0x80ae7a2  
169 -"Iteration started") at sched.c:856  
170 -856 }  
171 -....  
172 -(gdb) while thread != 0x40252ee4  
173 - >c  
174 - >end  
175 -....  
176 -....  
177 -Breakpoint 1, _st_show_thread_stack (thread=0x40252ee4, messg=0x0) at  
178 -sched.c:856  
179 -856 }  
180 -(gdb) bt  
181 -....  
182 -(gdb) # don't want to continue iteration, unset the flag:  
183 -(gdb) set _st_iterate_threads_flag = 0  
184 -(gdb) c  
185 -Continuing.  
186 -Breakpoint 1, _st_show_thread_stack (thread=0x0, messg=0x80ae78e "Iteration  
187 -completed")  
188 - at sched.c:856  
189 -856 }  
190 -(gdb) c  
191 -Continuing.  
192 -(gdb) return  
193 -Make selected stack frame return now? (y or n) y  
194 -#0 0x4011254e in __select ()  
195 - from /lib/libc.so.6  
196 -(gdb) detach  
197 -  
198 -  
199 -CHANGE LOG  
200 -  
201 -Changes from 1.8 to 1.9.  
202 -------------------------  
203 -o Support 32-bit and 64-bit Intel Macs.  
204 -  
205 -o Added ST_VERSION string, and ST_VERSION_MAJOR and ST_VERSION_MINOR  
206 - [bug 1796801].  
207 -  
208 -o Fixed some compiler warnings, based on a patch from Brian Wellington  
209 - [bug 1932741].  
210 -  
211 -  
212 -Changes from 1.7 to 1.8.  
213 ---------------------------  
214 -o Added support for kqueue and epoll on platforms that support them.  
215 - Added ability to choose the event notification system at program  
216 - startup.  
217 -  
218 -o Long-overdue public definitions of ST_UTIME_NO_TIMEOUT (-1ULL) and  
219 - ST_UTIME_NO_WAIT (0) [bug 1514436].  
220 -  
221 -o Documentation patch for st_utime() [bug 1514484].  
222 -  
223 -o Documentation patch for st_timecache_set() [bug 1514486].  
224 -  
225 -o Documentation patch for st_netfd_serialize_accept() [bug 1514494].  
226 -  
227 -o Added st_writev_resid() [rfe 1538344].  
228 -  
229 -o Added st_readv_resid() [rfe 1538768] and, for symmetry, st_readv().  
230 -  
231 -  
232 -Changes from 1.6 to 1.7.  
233 -------------------------  
234 -o Support glibc 2.4, which breaks programs that manipulate jump buffers.  
235 - Replaced Linux IA64 special cases with new md.S that covers all  
236 - Linux.  
237 -  
238 -  
239 -Changes from 1.5.2 to 1.6.  
240 ---------------------------  
241 -none  
242 -  
243 -  
244 -Changes from 1.5.1 to 1.5.2.  
245 -----------------------------  
246 -o Alfred Perlstein's context switch callback feature.  
247 -  
248 -o Claus Assmann's st_recvmsg/st_sendmsg wrappers.  
249 -  
250 -o Extra stack padding for platforms that need it.  
251 -  
252 -o Ron Arts's timeout clarifications in the reference manual.  
253 -  
254 -o Raymond Bero and Anton Berezin's AMD64 FreeBSD port.  
255 -  
256 -o Claus Assmann's AMD64 SunOS 5.10 port.  
257 -  
258 -o Claus Assmann's AMD64 OpenBSD port.  
259 -  
260 -o Michael Abd-El-Malek's Mac OS X port.  
261 -  
262 -o Michael Abd-El-Malek's stack printing patch.  
263 -  
264 -  
265 -Changes from 1.5.0 to 1.5.1.  
266 -----------------------------  
267 -o Andreas Gustafsson's USE_POLL fix.  
268 -  
269 -o Gene's st_set_utime_function() enhancement.  
270 -  
271 -  
272 -Changes from 1.4 to 1.5.0.  
273 ---------------------------  
274 -o Andreas Gustafsson's performance patch.  
275 -  
276 -o New extensions: Improved DNS resolver, generic LRU cache, in-process  
277 - DNS cache, and a program to test the resolver and cache.  
278 -  
279 -o Support for AMD Opteron 64-bit CPUs under Linux.  
280 -  
281 -o Support for SPARC-64 under Solaris.  
282 -  
283 -o Andreas Gustafsson's support for VAX under NetBSD.  
284 -  
285 -o Changed unportable #warning directives in md.h to #error.  
286 -  
287 -  
288 -Changes from 1.3 to 1.4.  
289 -------------------------  
290 -o Andreas Gustafsson's NetBSD port.  
291 -  
292 -o Wesley W. Terpstra's Darwin (MacOS X) port.  
293 -  
294 -o Support for many CPU architectures under Linux and *BSD.  
295 -  
296 -o Renamed private typedefs so they don't conflict with public ones any  
297 - more.  
298 -  
299 -o common.h now includes public.h for strict prototyping.  
300 -  
301 -o Joshua Levy's recommendation to make st_connect() and st_sendto()  
302 - accept const struct sockaddr pointers, as the originals do.  
303 -  
304 -o Clarified the documentation regarding blocking vs. non-blocking I/O.  
305 -  
306 -o Cygwin support.  
307 -  
308 -o Created the extensions directory.  
309 -  
310 -o Fixed warnings from ia64asm.S.  
311 -  
312 -  
313 -Changes from 1.2 to 1.3.  
314 -------------------------  
315 -o Added st_read_resid() and st_write_resid() to allow the caller to know  
316 - how much data was transferred before an error occurred. Updated  
317 - documentation.  
318 -  
319 -o Updated project link, copyrights, and documentation regarding  
320 - timeouts. Added comment to st_connect().  
321 -  
322 -o Optimized the _st_add_sleep_q() function in sched.c. Now we walk the  
323 - sleep queue *backward* when inserting a thread into it. When you  
324 - have lots (hundreds) of threads and several timeout values, it takes  
325 - a while to insert a thread at the appropriate point in the sleep  
326 - queue. The idea is that often this appropriate point is closer to  
327 - the end of the queue rather than the beginning. Measurements show  
328 - performance improves with this change. In any case this change  
329 - should do no harm.  
330 -  
331 -o Added a hint of when to define USE_POLL and when not to, to the  
332 - Makefile.  
333 -  
334 -o Added debugging support (files common.h and sched.c). See above.  
335 -  
336 -o Decreased the number of reallocations of _ST_POLLFDS in sched.c.  
337 - Inspired by Lev Walkin.  
338 -  
339 -o Fixed st_usleep(-1) and st_sleep(-1), and added a warning to the  
340 - documentation about too-large timeouts.  
341 -  
342 -o Linux/*BSD Alpha port.  
343 -  
344 -o Wesley W. Terpstra modernized the build process:  
345 - - properly build relocatable libraries under bsd and linux  
346 - - use library versioning  
347 - - added rpm spec file  
348 - - added debian/ files  
349 - See above for build instructions.  
350 -  
351 -  
352 -Changes from 1.1 to 1.2.  
353 -------------------------  
354 -o Added st_randomize_stacks().  
355 -  
356 -o Added a patch contributed by Sascha Schumann.  
357 -  
358 -  
359 -Changes from 1.0 to 1.1.  
360 -------------------------  
361 -o Relicensed under dual MPL-GPL.  
362 -  
363 -o OpenBSD port.  
364 -  
365 -o Compile-time option to use poll() instead of select() for  
366 - event polling (see Makefile).  
367 - This is useful if you want to support a large number of open  
368 - file descriptors (larger than FD_SETSIZE) within a single  
369 - process.  
370 -  
371 -o Linux IA-64 port.  
372 - Two issues make IA-64 different from other platforms:  
373 -  
374 - - Besides the traditional call stack in memory, IA-64 uses the  
375 - general register stack. Thus each thread needs a backing store  
376 - for the register stack in addition to the memory stack.  
377 -  
378 - - Current implementation of setjmp()/longjmp() can not be used  
379 - for thread context-switching since it assumes that only one  
380 - register stack exists. Using special assembly functions for  
381 - context-switching is unavoidable.  
382 -  
383 -o Thread stack capping on IRIX.  
384 - This allows some profiling tools (such as SpeedShop) to know when  
385 - to stop unwinding the stack. Without this libexc, used by SpeedShop,  
386 - traces right off the stack and crashes.  
387 -  
388 -o Miscellaneous documentation additions.  
389 -  
390 -  
391 -COPYRIGHTS  
392 -  
393 -Portions created by SGI are Copyright (C) 2000 Silicon Graphics, Inc.  
394 -All Rights Reserved.  
1 -<HTML>  
2 -<HEAD>  
3 -<TITLE>State Threads Library Programming Notes</TITLE>  
4 -</HEAD>  
5 -<BODY BGCOLOR=#FFFFFF>  
6 -<H2>Programming Notes</H2>  
7 -<P>  
8 -<B>  
9 -<UL>  
10 -<LI><A HREF=#porting>Porting</A></LI>  
11 -<LI><A HREF=#signals>Signals</A></LI>  
12 -<LI><A HREF=#intra>Intra-Process Synchronization</A></LI>  
13 -<LI><A HREF=#inter>Inter-Process Synchronization</A></LI>  
14 -<LI><A HREF=#nonnet>Non-Network I/O</A></LI>  
15 -<LI><A HREF=#timeouts>Timeouts</A></LI>  
16 -</UL>  
17 -</B>  
18 -<P>  
19 -<HR>  
20 -<P>  
21 -<A NAME="porting">  
22 -<H3>Porting</H3>  
23 -The State Threads library uses OS concepts that are available in some  
24 -form on most UNIX platforms, making the library very portable across  
25 -many flavors of UNIX. However, there are several parts of the library  
26 -that rely on platform-specific features. Here is the list of such parts:  
27 -<P>  
28 -<UL>  
29 -<LI><I>Thread context initialization</I>: Two ingredients of the  
30 -<TT>jmp_buf</TT>  
31 -data structure (the program counter and the stack pointer) have to be  
32 -manually set in the thread creation routine. The <TT>jmp_buf</TT> data  
33 -structure is defined in the <TT>setjmp.h</TT> header file and differs from  
34 -platform to platform. Usually the program counter is a structure member  
35 -with <TT>PC</TT> in the name and the stack pointer is a structure member  
36 -with <TT>SP</TT> in the name. One can also look in the  
37 -<A HREF="http://www.mozilla.org/source.html">Netscape's NSPR library source</A>  
38 -which already has this code for many UNIX-like platforms  
39 -(<TT>mozilla/nsprpub/pr/include/md/*.h</TT> files).  
40 -<P>  
41 -Note that on some BSD-derived platforms <TT>_setjmp(3)/_longjmp(3)</TT>  
42 -calls should be used instead of <TT>setjmp(3)/longjmp(3)</TT> (that is  
43 -the calls that manipulate only the stack and registers and do <I>not</I>  
44 -save and restore the process's signal mask).</LI>  
45 -<P>  
46 -Starting with glibc 2.4 on Linux the opacity of the <TT>jmp_buf</TT> data  
47 -structure is enforced by <TT>setjmp(3)/longjmp(3)</TT> so the  
48 -<TT>jmp_buf</TT> ingredients cannot be accessed directly anymore (unless  
49 -special environmental variable LD_POINTER_GUARD is set before application  
50 -execution). To avoid dependency on custom environment, the State Threads  
51 -library provides <TT>setjmp/longjmp</TT> replacement functions for  
52 -all Intel CPU architectures. Other CPU architectures can also be easily  
53 -supported (the <TT>setjmp/longjmp</TT> source code is widely available for  
54 -many CPU architectures).  
55 -<P>  
56 -<LI><I>High resolution time function</I>: Some platforms (IRIX, Solaris)  
57 -provide a high resolution time function based on the free running hardware  
58 -counter. This function returns the time counted since some arbitrary  
59 -moment in the past (usually machine power up time). It is not correlated in  
60 -any way to the time of day, and thus is not subject to resetting,  
61 -drifting, etc. This type of time is ideal for tasks where cheap, accurate  
62 -interval timing is required. If such a function is not available on a  
63 -particular platform, the <TT>gettimeofday(3)</TT> function can be used  
64 -(though on some platforms it involves a system call).  
65 -<P>  
66 -<LI><I>The stack growth direction</I>: The library needs to know whether the  
67 -stack grows toward lower (down) or higher (up) memory addresses.  
68 -One can write a simple test program that detects the stack growth direction  
69 -on a particular platform.</LI>  
70 -<P>  
71 -<LI><I>Non-blocking attribute inheritance</I>: On some platforms (e.g. IRIX)  
72 -the socket created as a result of the <TT>accept(2)</TT> call inherits the  
73 -non-blocking attribute of the listening socket. One needs to consult the manual  
74 -pages or write a simple test program to see if this applies to a specific  
75 -platform.</LI>  
76 -<P>  
77 -<LI><I>Anonymous memory mapping</I>: The library allocates memory segments  
78 -for thread stacks by doing anonymous memory mapping (<TT>mmap(2)</TT>). This  
79 -mapping is somewhat different on SVR4 and BSD4.3 derived platforms.  
80 -<P>  
81 -The memory mapping can be avoided altogether by using <TT>malloc(3)</TT> for  
82 -stack allocation. In this case the <TT>MALLOC_STACK</TT> macro should be  
83 -defined.</LI>  
84 -</UL>  
85 -<P>  
86 -All machine-dependent feature test macros should be defined in the  
87 -<TT>md.h</TT> header file. The assembly code for <TT>setjmp/longjmp</TT>  
88 -replacement functions for all CPU architectures should be placed in  
89 -the <TT>md.S</TT> file.  
90 -<P>  
91 -The current version of the library is ported to:  
92 -<UL>  
93 - <LI>IRIX 6.x (both 32 and 64 bit)</LI>  
94 - <LI>Linux (kernel 2.x and glibc 2.x) on x86, Alpha, MIPS and MIPSEL,  
95 - SPARC, ARM, PowerPC, 68k, HPPA, S390, IA-64, and Opteron (AMD-64)</LI>  
96 - <LI>Solaris 2.x (SunOS 5.x) on x86, AMD64, SPARC, and SPARC-64</LI>  
97 - <LI>AIX 4.x</LI>  
98 - <LI>HP-UX 11 (both 32 and 64 bit)</LI>  
99 - <LI>Tru64/OSF1</LI>  
100 - <LI>FreeBSD on x86, AMD64, and Alpha</LI>  
101 - <LI>OpenBSD on x86, AMD64, Alpha, and SPARC</LI>  
102 - <LI>NetBSD on x86, Alpha, SPARC, and VAX</LI>  
103 - <LI>MacOS X (Darwin) on PowerPC (32 bit) and Intel (both 32 and 64 bit) [universal]</LI>  
104 - <LI>Cygwin</LI>  
105 -</UL>  
106 -<P>  
107 -  
108 -<A NAME="signals">  
109 -<H3>Signals</H3>  
110 -Signal handling in an application using State Threads should be treated the  
111 -same way as in a classical UNIX process application. There is no such  
112 -thing as per-thread signal mask, all threads share the same signal handlers,  
113 -and only asynchronous-safe functions can be used in signal handlers.  
114 -However, there is a way to process signals synchronously by converting a  
115 -signal event to an I/O event: a signal catching function does a write to  
116 -a pipe which will be processed synchronously by a dedicated signal handling  
117 -thread. The following code demonstrates this technique (error handling is  
118 -omitted for clarity):  
119 -<PRE>  
120 -  
121 -/* Per-process pipe which is used as a signal queue. */  
122 -/* Up to PIPE_BUF/sizeof(int) signals can be queued up. */  
123 -int sig_pipe[2];  
124 -  
125 -/* Signal catching function. */  
126 -/* Converts signal event to I/O event. */  
127 -void sig_catcher(int signo)  
128 -{  
129 - int err;  
130 -  
131 - /* Save errno to restore it after the write() */  
132 - err = errno;  
133 - /* write() is reentrant/async-safe */  
134 - write(sig_pipe[1], &signo, sizeof(int));  
135 - errno = err;  
136 -}  
137 -  
138 -/* Signal processing function. */  
139 -/* This is the "main" function of the signal processing thread. */  
140 -void *sig_process(void *arg)  
141 -{  
142 - st_netfd_t nfd;  
143 - int signo;  
144 -  
145 - nfd = st_netfd_open(sig_pipe[0]);  
146 -  
147 - for ( ; ; ) {  
148 - /* Read the next signal from the pipe */  
149 - st_read(nfd, &signo, sizeof(int), ST_UTIME_NO_TIMEOUT);  
150 -  
151 - /* Process signal synchronously */  
152 - switch (signo) {  
153 - case SIGHUP:  
154 - /* do something here - reread config files, etc. */  
155 - break;  
156 - case SIGTERM:  
157 - /* do something here - cleanup, etc. */  
158 - break;  
159 - /* .  
160 - .  
161 - Other signals  
162 - .  
163 - .  
164 - */  
165 - }  
166 - }  
167 -  
168 - return NULL;  
169 -}  
170 -  
171 -int main(int argc, char *argv[])  
172 -{  
173 - struct sigaction sa;  
174 - .  
175 - .  
176 - .  
177 -  
178 - /* Create signal pipe */  
179 - pipe(sig_pipe);  
180 -  
181 - /* Create signal processing thread */  
182 - st_thread_create(sig_process, NULL, 0, 0);  
183 -  
184 - /* Install sig_catcher() as a signal handler */  
185 - sa.sa_handler = sig_catcher;  
186 - sigemptyset(&sa.sa_mask);  
187 - sa.sa_flags = 0;  
188 - sigaction(SIGHUP, &sa, NULL);  
189 -  
190 - sa.sa_handler = sig_catcher;  
191 - sigemptyset(&sa.sa_mask);  
192 - sa.sa_flags = 0;  
193 - sigaction(SIGTERM, &sa, NULL);  
194 -  
195 - .  
196 - .  
197 - .  
198 -  
199 -}  
200 -  
201 -</PRE>  
202 -<P>  
203 -Note that if multiple processes are used (see below), the signal pipe should  
204 -be initialized after the <TT>fork(2)</TT> call so that each process has its  
205 -own private pipe.  
206 -<P>  
207 -  
208 -<A NAME="intra">  
209 -<H3>Intra-Process Synchronization</H3>  
210 -Due to the event-driven nature of the library scheduler, the thread context  
211 -switch (process state change) can only happen in a well-known set of  
212 -library functions. This set includes functions in which a thread may  
213 -"block":<TT> </TT>I/O functions (<TT>st_read(), st_write(), </TT>etc.),  
214 -sleep functions (<TT>st_sleep(), </TT>etc.), and thread synchronization  
215 -functions (<TT>st_thread_join(), st_cond_wait(), </TT>etc.). As a result,  
216 -process-specific global data need not to be protected by locks since a thread  
217 -cannot be rescheduled while in a critical section (and only one thread at a  
218 -time can access the same memory location). By the same token,  
219 -non thread-safe functions (in a traditional sense) can be safely used with  
220 -the State Threads. The library's mutex facilities are practically useless  
221 -for a correctly written application (no blocking functions in critical  
222 -section) and are provided mostly for completeness. This absence of locking  
223 -greatly simplifies an application design and provides a foundation for  
224 -scalability.  
225 -<P>  
226 -  
227 -<A NAME="inter">  
228 -<H3>Inter-Process Synchronization</H3>  
229 -The State Threads library makes it possible to multiplex a large number  
230 -of simultaneous connections onto a much smaller number of separate  
231 -processes, where each process uses a many-to-one user-level threading  
232 -implementation (<B>N</B> of <B>M:1</B> mappings rather than one <B>M:N</B>  
233 -mapping used in native threading libraries on some platforms). This design  
234 -is key to the application's scalability. One can think about it as if a  
235 -set of all threads is partitioned into separate groups (processes) where  
236 -each group has a separate pool of resources (virtual address space, file  
237 -descriptors, etc.). An application designer has full control of how many  
238 -groups (processes) an application creates and what resources, if any,  
239 -are shared among different groups via standard UNIX inter-process  
240 -communication (IPC) facilities.<P>  
241 -There are several reasons for creating multiple processes:  
242 -<P>  
243 -<UL>  
244 -<LI>To take advantage of multiple hardware entities (CPUs, disks, etc.)  
245 -available in the system (hardware parallelism).</LI>  
246 -<P>  
247 -<LI>To reduce risk of losing a large number of user connections when one of  
248 -the processes crashes. For example, if <B>C</B> user connections (threads)  
249 -are multiplexed onto <B>P</B> processes and one of the processes crashes,  
250 -only a fraction (<B>C/P</B>) of all connections will be lost.</LI>  
251 -<P>  
252 -<LI>To overcome per-process resource limitations imposed by the OS. For  
253 -example, if <TT>select(2)</TT> is used for event polling, the number of  
254 -simultaneous connections (threads) per process is  
255 -limited by the <TT>FD_SETSIZE</TT> parameter (see <TT>select(2)</TT>).  
256 -If <TT>FD_SETSIZE</TT> is equal to 1024 and each connection needs one file  
257 -descriptor, then an application should create 10 processes to support 10,000  
258 -simultaneous connections.</LI>  
259 -</UL>  
260 -<P>  
261 -Ideally all user sessions are completely independent, so there is no need for  
262 -inter-process communication. It is always better to have several separate  
263 -smaller process-specific resources (e.g., data caches) than to have one large  
264 -resource shared (and modified) by all processes. Sometimes, however, there  
265 -is a need to share a common resource among different processes. In that case,  
266 -standard UNIX IPC facilities can be used. In addition to that, there is a way  
267 -to synchronize different processes so that only the thread accessing the  
268 -shared resource will be suspended (but not the entire process) if that resource  
269 -is unavailable. In the following code fragment a pipe is used as a counting  
270 -semaphore for inter-process synchronization:  
271 -<PRE>  
272 -#ifndef PIPE_BUF  
273 -#define PIPE_BUF 512 /* POSIX */  
274 -#endif  
275 -  
276 -/* Semaphore data structure */  
277 -typedef struct ipc_sem {  
278 - st_netfd_t rdfd; /* read descriptor */  
279 - st_netfd_t wrfd; /* write descriptor */  
280 -} ipc_sem_t;  
281 -  
282 -/* Create and initialize the semaphore. Should be called before fork(2). */  
283 -/* 'value' must be less than PIPE_BUF. */  
284 -/* If 'value' is 1, the semaphore works as mutex. */  
285 -ipc_sem_t *ipc_sem_create(int value)  
286 -{  
287 - ipc_sem_t *sem;  
288 - int p[2];  
289 - char b[PIPE_BUF];  
290 -  
291 - /* Error checking is omitted for clarity */  
292 - sem = malloc(sizeof(ipc_sem_t));  
293 -  
294 - /* Create the pipe */  
295 - pipe(p);  
296 - sem->rdfd = st_netfd_open(p[0]);  
297 - sem->wrfd = st_netfd_open(p[1]);  
298 -  
299 - /* Initialize the semaphore: put 'value' bytes into the pipe */  
300 - write(p[1], b, value);  
301 -  
302 - return sem;  
303 -}  
304 -  
305 -/* Try to decrement the "value" of the semaphore. */  
306 -/* If "value" is 0, the calling thread blocks on the semaphore. */  
307 -int ipc_sem_wait(ipc_sem_t *sem)  
308 -{  
309 - char c;  
310 -  
311 - /* Read one byte from the pipe */  
312 - if (st_read(sem->rdfd, &c, 1, ST_UTIME_NO_TIMEOUT) != 1)  
313 - return -1;  
314 -  
315 - return 0;  
316 -}  
317 -  
318 -/* Increment the "value" of the semaphore. */  
319 -int ipc_sem_post(ipc_sem_t *sem)  
320 -{  
321 - char c;  
322 -  
323 - if (st_write(sem->wrfd, &c, 1, ST_UTIME_NO_TIMEOUT) != 1)  
324 - return -1;  
325 -  
326 - return 0;  
327 -}  
328 -  
329 -</PRE>  
330 -<P>  
331 -  
332 -Generally, the following steps should be followed when writing an application  
333 -using the State Threads library:  
334 -<P>  
335 -<OL>  
336 -<LI>Initialize the library (<TT>st_init()</TT>).</LI>  
337 -<P>  
338 -<LI>Create resources that will be shared among different processes:  
339 - create and bind listening sockets, create shared memory segments, IPC  
340 - channels, synchronization primitives, etc.</LI>  
341 -<P>  
342 -<LI>Create several processes (<TT>fork(2)</TT>). The parent process should  
343 - either exit or become a "watchdog" (e.g., it starts a new process when  
344 - an existing one crashes, does a cleanup upon application termination,  
345 - etc.).</LI>  
346 -<P>  
347 -<LI>In each child process create a pool of threads  
348 - (<TT>st_thread_create()</TT>) to handle user connections.</LI>  
349 -</OL>  
350 -<P>  
351 -  
352 -<A NAME="nonnet">  
353 -<H3>Non-Network I/O</H3>  
354 -  
355 -The State Threads architecture uses non-blocking I/O on  
356 -<TT>st_netfd_t</TT> objects for concurrent processing of multiple user  
357 -connections. This architecture has a drawback: the entire process and  
358 -all its threads may block for the duration of a <I>disk</I> or other  
359 -non-network I/O operation, whether through State Threads I/O functions,  
360 -direct system calls, or standard I/O functions. (This is applicable  
361 -mostly to disk <I>reads</I>; disk <I>writes</I> are usually performed  
362 -asynchronously -- data goes to the buffer cache to be written to disk  
363 -later.) Fortunately, disk I/O (unlike network I/O) usually takes a  
364 -finite and predictable amount of time, but this may not be true for  
365 -special devices or user input devices (including stdin). Nevertheless,  
366 -such I/O reduces throughput of the system and increases response times.  
367 -There are several ways to design an application to overcome this  
368 -drawback:  
369 -  
370 -<P>  
371 -<UL>  
372 -<LI>Create several identical main processes as described above (symmetric  
373 - architecture). This will improve CPU utilization and thus improve the  
374 - overall throughput of the system.</LI>  
375 -<P>  
376 -<LI>Create multiple "helper" processes in addition to the main process that  
377 - will handle blocking I/O operations (asymmetric architecture).  
378 - This approach was suggested for Web servers in a  
379 - <A HREF="http://www.cs.rice.edu/~vivek/flash99/">paper</A> by Peter  
380 - Druschel et al. In this architecture the main process communicates with  
381 - a helper process via an IPC channel (<TT>pipe(2), socketpair(2)</TT>).  
382 - The main process instructs a helper to perform the potentially blocking  
383 - operation. Once the operation completes, the helper returns a  
384 - notification via IPC.  
385 -</UL>  
386 -<P>  
387 -  
388 -<A NAME="timeouts">  
389 -<H3>Timeouts</H3>  
390 -  
391 -The <TT>timeout</TT> parameter to <TT>st_cond_timedwait()</TT> and the  
392 -I/O functions, and the arguments to <TT>st_sleep()</TT> and  
393 -<TT>st_usleep()</TT> specify a maximum time to wait <I>since the last  
394 -context switch</I> not since the beginning of the function call.  
395 -  
396 -<P>The State Threads' time resolution is actually the time interval  
397 -between context switches. That time interval may be large in some  
398 -situations, for example, when a single thread does a lot of work  
399 -continuously. Note that a steady, uninterrupted stream of network I/O  
400 -qualifies for this description; a context switch occurs only when a  
401 -thread blocks.  
402 -  
403 -<P>If a specified I/O timeout is less than the time interval between  
404 -context switches the function may return with a timeout error before  
405 -that amount of time has elapsed since the beginning of the function  
406 -call. For example, if eight milliseconds have passed since the last  
407 -context switch and an I/O function with a timeout of 10 milliseconds  
408 -blocks, causing a switch, the call may return with a timeout error as  
409 -little as two milliseconds after it was called. (On Linux,  
410 -<TT>select()</TT>'s timeout is an <I>upper</I> bound on the amount of  
411 -time elapsed before select returns.) Similarly, if 12 ms have passed  
412 -already, the function may return immediately.  
413 -  
414 -<P>In almost all cases I/O timeouts should be used only for detecting a  
415 -broken network connection or for preventing a peer from holding an idle  
416 -connection for too long. Therefore for most applications realistic I/O  
417 -timeouts should be on the order of seconds. Furthermore, there's  
418 -probably no point in retrying operations that time out. Rather than  
419 -retrying simply use a larger timeout in the first place.  
420 -  
421 -<P>The largest valid timeout value is platform-dependent and may be  
422 -significantly less than <TT>INT_MAX</TT> seconds for <TT>select()</TT>  
423 -or <TT>INT_MAX</TT> milliseconds for <TT>poll()</TT>. Generally, you  
424 -should not use timeouts exceeding several hours. Use  
425 -<tt>ST_UTIME_NO_TIMEOUT</tt> (<tt>-1</tt>) as a special value to  
426 -indicate infinite timeout or indefinite sleep. Use  
427 -<tt>ST_UTIME_NO_WAIT</tt> (<tt>0</tt>) to indicate no waiting at all.  
428 -  
429 -<P>  
430 -<HR>  
431 -<P>  
432 -</BODY>  
433 -</HTML>  
434 -  
1 -<HTML>  
2 -<HEAD>  
3 -<TITLE>State Threads for Internet Applications</TITLE>  
4 -</HEAD>  
5 -<BODY BGCOLOR=#FFFFFF>  
6 -<H2>State Threads for Internet Applications</H2>  
7 -<H3>Introduction</H3>  
8 -<P>  
9 -State Threads is an application library which provides a  
10 -foundation for writing fast and highly scalable Internet Applications  
11 -on UNIX-like platforms. It combines the simplicity of the multithreaded  
12 -programming paradigm, in which one thread supports each simultaneous  
13 -connection, with the performance and scalability of an event-driven  
14 -state machine architecture.</P>  
15 -  
16 -<H3>1. Definitions</H3>  
17 -<P>  
18 -<A NAME="IA">  
19 -<H4>1.1 Internet Applications</H4>  
20 -</A>  
21 -<P>  
22 -An <I>Internet Application</I> (IA) is either a server or client network  
23 -application that accepts connections from clients and may or may not  
24 -connect to servers. In an IA the arrival or departure of network data  
25 -often controls processing (that is, IA is a <I>data-driven</I> application).  
26 -For each connection, an IA does some finite amount of work  
27 -involving data exchange with its peer, where its peer may be either  
28 -a client or a server.  
29 -The typical transaction steps of an IA are to accept a connection,  
30 -read a request, do some finite and predictable amount of work to  
31 -process the request, then write a response to the peer that sent the  
32 -request. One example of an IA is a Web server;  
33 -the most general example of an IA is a proxy server, because it both  
34 -accepts connections from clients and connects to other servers.</P>  
35 -<P>  
36 -We assume that the performance of an IA is constrained by available CPU  
37 -cycles rather than network bandwidth or disk I/O (that is, CPU  
38 -is a bottleneck resource).  
39 -<P>  
40 -  
41 -<A NAME="PS">  
42 -<H4>1.2 Performance and Scalability</H4>  
43 -</A>  
44 -<P>  
45 -The <I>performance</I> of an IA is usually evaluated as its  
46 -throughput measured in transactions per second or bytes per second (one  
47 -can be converted to the other, given the average transaction size). There are  
48 -several benchmarks that can be used to measure throughput of Web serving  
49 -applications for specific workloads (such as  
50 -<A HREF="http://www.spec.org/osg/web96/">SPECweb96</A>,  
51 -<A HREF="http://www.mindcraft.com/webstone/">WebStone</A>,  
52 -<A HREF="http://www.zdnet.com/zdbop/webbench/">WebBench</A>).  
53 -Although there is no common definition for <I>scalability</I>, in general it  
54 -expresses the ability of an application to sustain its performance when some  
55 -external condition changes. For IAs this external condition is either the  
56 -number of clients (also known as "users," "simultaneous connections," or "load  
57 -generators") or the underlying hardware system size (number of CPUs, memory  
58 -size, and so on). Thus there are two types of scalability: <I>load  
59 -scalability</I> and <I>system scalability</I>, respectively.  
60 -<P>  
61 -The figure below shows how the throughput of an idealized IA changes with  
62 -the increasing number of clients (solid blue line). Initially the throughput  
63 -grows linearly (the slope represents the maximal throughput that one client  
64 -can provide). Within this initial range, the IA is underutilized and CPUs are  
65 -partially idle. Further increase in the number of clients leads to a system  
66 -saturation, and the throughput gradually stops growing as all CPUs become fully  
67 -utilized. After that point, the throughput stays flat because there are no  
68 -more CPU cycles available.  
69 -In the real world, however, each simultaneous connection  
70 -consumes some computational and memory resources, even when idle, and this  
71 -overhead grows with the number of clients. Therefore, the throughput of the  
72 -real world IA starts dropping after some point (dashed blue line in the figure  
73 -below). The rate at which the throughput drops depends, among other things, on  
74 -application design.  
75 -<P>  
76 -We say that an application has a good <I>load scalability</I> if it can  
77 -sustain its throughput over a wide range of loads.  
78 -Interestingly, the <A HREF="http://www.spec.org/osg/web99/">SPECweb99</A>  
79 -benchmark somewhat reflects the Web server's load scalability because it  
80 -measures the number of clients (load generators) given a mandatory minimal  
81 -throughput per client (that is, it measures the server's <I>capacity</I>).  
82 -This is unlike <A HREF="http://www.spec.org/osg/web96/">SPECweb96</A> and  
83 -other benchmarks that use the throughput as their main metric (see the figure  
84 -below).  
85 -<P>  
86 -<CENTER><IMG SRC="fig.gif" ALT="Figure: Throughput vs. Number of clients">  
87 -</CENTER>  
88 -<P>  
89 -<I>System scalability</I> is the ability of an application to sustain its  
90 -performance per hardware unit (such as a CPU) with the increasing number of  
91 -these units. In other words, good system scalability means that doubling the  
92 -number of processors will roughly double the application's throughput (dashed  
93 -green line). We assume here that the underlying operating system also scales  
94 -well. Good system scalability allows you to initially run an application on  
95 -the smallest system possible, while retaining the ability to move that  
96 -application to a larger system if necessary, without excessive effort or  
97 -expense. That is, an application need not be rewritten or even undergo a  
98 -major porting effort when changing system size.  
99 -<P>  
100 -Although scalability and performance are more important in the case of server  
101 -IAs, they should also be considered for some client applications (such as  
102 -benchmark load generators).  
103 -<P>  
104 -  
105 -<A NAME="CONC">  
106 -<H4>1.3 Concurrency</H4>  
107 -</A>  
108 -<P>  
109 -Concurrency reflects the parallelism in a system. The two unrelated types  
110 -are <I>virtual</I> concurrency and <I>real</I> concurrency.  
111 -<UL>  
112 -<LI>Virtual (or apparent) concurrency is the number of simultaneous  
113 -connections that a system supports.  
114 -<BR><BR>  
115 -<LI>Real concurrency is the number of hardware devices, including  
116 -CPUs, network cards, and disks, that actually allow a system to perform  
117 -tasks in parallel.  
118 -</UL>  
119 -<P>  
120 -An IA must provide virtual concurrency in order to serve many users  
121 -simultaneously.  
122 -To achieve maximum performance and scalability in doing so, the number of  
123 -programming entities than an IA creates to be scheduled by the OS kernel  
124 -should be  
125 -kept close to (within an order of magnitude of) the real concurrency found on  
126 -the system. These programming entities scheduled by the kernel are known as  
127 -<I>kernel execution vehicles</I>. Examples of kernel execution vehicles  
128 -include Solaris lightweight processes and IRIX kernel threads.  
129 -In other words, the number of kernel execution vehicles should be dictated by  
130 -the system size and not by the number of simultaneous connections.  
131 -<P>  
132 -  
133 -<H3>2. Existing Architectures</H3>  
134 -<P>  
135 -There are a few different architectures that are commonly used by IAs.  
136 -These include the <I>Multi-Process</I>,  
137 -<I>Multi-Threaded</I>, and <I>Event-Driven State Machine</I>  
138 -architectures.  
139 -<P>  
140 -<A NAME="MP">  
141 -<H4>2.1 Multi-Process Architecture</H4>  
142 -</A>  
143 -<P>  
144 -In the Multi-Process (MP) architecture, an individual process is  
145 -dedicated to each simultaneous connection.  
146 -A process performs all of a transaction's initialization steps  
147 -and services a connection completely before moving on to service  
148 -a new connection.  
149 -<P>  
150 -User sessions in IAs are relatively independent; therefore, no  
151 -synchronization between processes handling different connections is  
152 -necessary. Because each process has its own private address space,  
153 -this architecture is very robust. If a process serving one of the connections  
154 -crashes, the other sessions will not be affected. However, to serve many  
155 -concurrent connections, an equal number of processes must be employed.  
156 -Because processes are kernel entities (and are in fact the heaviest ones),  
157 -the number of kernel entities will be at least as large as the number of  
158 -concurrent sessions. On most systems, good performance will not be achieved  
159 -when more than a few hundred processes are created because of the high  
160 -context-switching overhead. In other words, MP applications have poor load  
161 -scalability.  
162 -<P>  
163 -On the other hand, MP applications have very good system scalability, because  
164 -no resources are shared among different processes and there is no  
165 -synchronization overhead.  
166 -<P>  
167 -The Apache Web Server 1.x (<A HREF=#refs1>[Reference 1]</A>) uses the MP  
168 -architecture on UNIX systems.  
169 -<P>  
170 -<A NAME="MT">  
171 -<H4>2.2 Multi-Threaded Architecture</H4>  
172 -</A>  
173 -<P>  
174 -In the Multi-Threaded (MT) architecture, multiple independent threads  
175 -of control are employed within a single shared address space. Like a  
176 -process in the MP architecture, each thread performs all of a  
177 -transaction's initialization steps and services a connection completely  
178 -before moving on to service a new connection.  
179 -<P>  
180 -Many modern UNIX operating systems implement a <I>many-to-few</I> model when  
181 -mapping user-level threads to kernel entities. In this model, an  
182 -arbitrarily large number of user-level threads is multiplexed onto a  
183 -lesser number of kernel execution vehicles. Kernel execution  
184 -vehicles are also known as <I>virtual processors</I>. Whenever a user-level  
185 -thread makes a blocking system call, the kernel execution vehicle it is using  
186 -will become blocked in the kernel. If there are no other non-blocked kernel  
187 -execution vehicles and there are other runnable user-level threads, a new  
188 -kernel execution vehicle will be created automatically. This prevents the  
189 -application from blocking when it can continue to make useful forward  
190 -progress.  
191 -<P>  
192 -Because IAs are by nature network I/O driven, all concurrent sessions block on  
193 -network I/O at various points. As a result, the number of virtual processors  
194 -created in the kernel grows close to the number of user-level threads  
195 -(or simultaneous connections). When this occurs, the many-to-few model  
196 -effectively degenerates to a <I>one-to-one</I> model. Again, like in  
197 -the MP architecture, the number of kernel execution vehicles is dictated by  
198 -the number of simultaneous connections rather than by number of CPUs. This  
199 -reduces an application's load scalability. However, because kernel threads  
200 -(lightweight processes) use fewer resources and are more light-weight than  
201 -traditional UNIX processes, an MT application should scale better with load  
202 -than an MP application.  
203 -<P>  
204 -Unexpectedly, the small number of virtual processors sharing the same address  
205 -space in the MT architecture destroys an application's system scalability  
206 -because of contention among the threads on various locks. Even if an  
207 -application itself is carefully  
208 -optimized to avoid lock contention around its own global data (a non-trivial  
209 -task), there are still standard library functions and system calls  
210 -that use common resources hidden from the application. For example,  
211 -on many platforms thread safety of memory allocation routines  
212 -(<TT>malloc(3)</TT>, <TT>free(3)</TT>, and so on) is achieved by using a single  
213 -global lock. Another example is a per-process file descriptor table.  
214 -This common resource table is shared by all kernel execution vehicles within  
215 -the same process and must be protected when one modifies it via  
216 -certain system calls (such as <TT>open(2)</TT>, <TT>close(2)</TT>, and so on).  
217 -In addition to that, maintaining the caches coherent  
218 -among CPUs on multiprocessor systems hurts performance when different threads  
219 -running on different CPUs modify data items on the same cache line.  
220 -<P>  
221 -In order to improve load scalability, some applications employ a different  
222 -type of MT architecture: they create one or more thread(s) <I>per task</I>  
223 -rather than one thread <I>per connection</I>. For example, one small group  
224 -of threads may be responsible for accepting client connections, another  
225 -for request processing, and yet another for serving responses. The main  
226 -advantage of this architecture is that it eliminates the tight coupling  
227 -between the number of threads and number of simultaneous connections. However,  
228 -in this architecture, different task-specific thread groups must share common  
229 -work queues that must be protected by mutual exclusion locks (a typical  
230 -producer-consumer problem). This adds synchronization overhead that causes an  
231 -application to perform badly on multiprocessor systems. In other words, in  
232 -this architecture, the application's system scalability is sacrificed for the  
233 -sake of load scalability.  
234 -<P>  
235 -Of course, the usual nightmares of threaded programming, including data  
236 -corruption, deadlocks, and race conditions, also make MT architecture (in any  
237 -form) non-simplistic to use.  
238 -<P>  
239 -  
240 -<A NAME="EDSM">  
241 -<H4>2.3 Event-Driven State Machine Architecture</H4>  
242 -</A>  
243 -<P>  
244 -In the Event-Driven State Machine (EDSM) architecture, a single process  
245 -is employed to concurrently process multiple connections. The basics of this  
246 -architecture are described in Comer and Stevens  
247 -<A HREF=#refs2>[Reference 2]</A>.  
248 -The EDSM architecture performs one basic data-driven step associated with  
249 -a particular connection at a time, thus multiplexing many concurrent  
250 -connections. The process operates as a state machine that receives an event  
251 -and then reacts to it.  
252 -<P>  
253 -In the idle state the EDSM calls <TT>select(2)</TT> or <TT>poll(2)</TT> to  
254 -wait for network I/O events. When a particular file descriptor is ready for  
255 -I/O, the EDSM completes the corresponding basic step (usually by invoking a  
256 -handler function) and starts the next one. This architecture uses  
257 -non-blocking system calls to perform asynchronous network I/O operations.  
258 -For more details on non-blocking I/O see Stevens  
259 -<A HREF=#refs3>[Reference 3]</A>.  
260 -<P>  
261 -To take advantage of hardware parallelism (real concurrency), multiple  
262 -identical processes may be created. This is called Symmetric Multi-Process  
263 -EDSM and is used, for example, in the Zeus Web Server  
264 -(<A HREF=#refs4>[Reference 4]</A>). To more efficiently multiplex disk I/O,  
265 -special "helper" processes may be created. This is called Asymmetric  
266 -Multi-Process EDSM and was proposed for Web servers by Druschel  
267 -and others <A HREF=#refs5>[Reference 5]</A>.  
268 -<P>  
269 -EDSM is probably the most scalable architecture for IAs.  
270 -Because the number of simultaneous connections (virtual concurrency) is  
271 -completely decoupled from the number of kernel execution vehicles (processes),  
272 -this architecture has very good load scalability. It requires only minimal  
273 -user-level resources to create and maintain additional connection.  
274 -<P>  
275 -Like MP applications, Multi-Process EDSM has very good system scalability  
276 -because no resources are shared among different processes and there is no  
277 -synchronization overhead.  
278 -<P>  
279 -Unfortunately, the EDSM architecture is monolithic rather than based on the  
280 -concept of threads, so new applications generally need to be implemented from  
281 -the ground up. In effect, the EDSM architecture simulates threads and their  
282 -stacks the hard way.  
283 -<P>  
284 -  
285 -<A NAME="ST">  
286 -<H3>3. State Threads Library</H3>  
287 -</A>  
288 -<P>  
289 -The State Threads library combines the advantages of all of the above  
290 -architectures. The interface preserves the programming simplicity of thread  
291 -abstraction, allowing each simultaneous connection to be treated as a separate  
292 -thread of execution within a single process. The underlying implementation is  
293 -close to the EDSM architecture as the state of each particular concurrent  
294 -session is saved in a separate memory segment.  
295 -<P>  
296 -  
297 -<H4>3.1 State Changes and Scheduling</H4>  
298 -<P>  
299 -The state of each concurrent session includes its stack environment  
300 -(stack pointer, program counter, CPU registers) and its stack. Conceptually,  
301 -a thread context switch can be viewed as a process changing its state. There  
302 -are no kernel entities involved other than processes.  
303 -Unlike other general-purpose threading libraries, the State Threads library  
304 -is fully deterministic. The thread context switch (process state change) can  
305 -only happen in a well-known set of functions (at I/O points or at explicit  
306 -synchronization points). As a result, process-specific global data does not  
307 -have to be protected by mutual exclusion locks in most cases. The entire  
308 -application is free to use all the static variables and non-reentrant library  
309 -functions it wants, greatly simplifying programming and debugging while  
310 -increasing performance. This is somewhat similar to a <I>co-routine</I> model  
311 -(co-operatively multitasked threads), except that no explicit yield is needed  
312 ---  
313 -sooner or later, a thread performs a blocking I/O operation and thus surrenders  
314 -control. All threads of execution (simultaneous connections) have the  
315 -same priority, so scheduling is non-preemptive, like in the EDSM architecture.  
316 -Because IAs are data-driven (processing is limited by the size of network  
317 -buffers and data arrival rates), scheduling is non-time-slicing.  
318 -<P>  
319 -Only two types of external events are handled by the library's  
320 -scheduler, because only these events can be detected by  
321 -<TT>select(2)</TT> or <TT>poll(2)</TT>: I/O events (a file descriptor is ready  
322 -for I/O) and time events  
323 -(some timeout has expired). However, other types of events (such as  
324 -a signal sent to a process) can also be handled by converting them to I/O  
325 -events. For example, a signal handling function can perform a write to a pipe  
326 -(<TT>write(2)</TT> is reentrant/asynchronous-safe), thus converting a signal  
327 -event to an I/O event.  
328 -<P>  
329 -To take advantage of hardware parallelism, as in the EDSM architecture,  
330 -multiple processes can be created in either a symmetric or asymmetric manner.  
331 -Process management is not in the library's scope but instead is left up to the  
332 -application.  
333 -<P>  
334 -There are several general-purpose threading libraries that implement a  
335 -<I>many-to-one</I> model (many user-level threads to one kernel execution  
336 -vehicle), using the same basic techniques as the State Threads library  
337 -(non-blocking I/O, event-driven scheduler, and so on). For an example, see GNU  
338 -Portable Threads (<A HREF=#refs6>[Reference 6]</A>). Because they are  
339 -general-purpose, these libraries have different objectives than the State  
340 -Threads library. The State Threads library is <I>not</I> a general-purpose  
341 -threading library,  
342 -but rather an application library that targets only certain types of  
343 -applications (IAs) in order to achieve the highest possible performance and  
344 -scalability for those applications.  
345 -<P>  
346 -  
347 -<H4>3.2 Scalability</H4>  
348 -<P>  
349 -State threads are very lightweight user-level entities, and therefore creating  
350 -and maintaining user connections requires minimal resources. An application  
351 -using the State Threads library scales very well with the increasing number  
352 -of connections.  
353 -<P>  
354 -On multiprocessor systems an application should create multiple processes  
355 -to take advantage of hardware parallelism. Using multiple separate processes  
356 -is the <I>only</I> way to achieve the highest possible system scalability.  
357 -This is because duplicating per-process resources is the only way to avoid  
358 -significant synchronization overhead on multiprocessor systems. Creating  
359 -separate UNIX processes naturally offers resource duplication. Again,  
360 -as in the EDSM architecture, there is no connection between the number of  
361 -simultaneous connections (which may be very large and changes within a wide  
362 -range) and the number of kernel entities (which is usually small and constant).  
363 -In other words, the State Threads library makes it possible to multiplex a  
364 -large number of simultaneous connections onto a much smaller number of  
365 -separate processes, thus allowing an application to scale well with both  
366 -the load and system size.  
367 -<P>  
368 -  
369 -<H4>3.3 Performance</H4>  
370 -<P>  
371 -Performance is one of the library's main objectives. The State Threads  
372 -library is implemented to minimize the number of system calls and  
373 -to make thread creation and context switching as fast as possible.  
374 -For example, per-thread signal mask does not exist (unlike  
375 -POSIX threads), so there is no need to save and restore a process's  
376 -signal mask on every thread context switch. This eliminates two system  
377 -calls per context switch. Signal events can be handled much more  
378 -efficiently by converting them to I/O events (see above).  
379 -<P>  
380 -  
381 -<H4>3.4 Portability</H4>  
382 -<P>  
383 -The library uses the same general, underlying concepts as the EDSM  
384 -architecture, including non-blocking I/O, file descriptors, and  
385 -I/O multiplexing. These concepts are available in some form on most  
386 -UNIX platforms, making the library very portable across many  
387 -flavors of UNIX. There are only a few platform-dependent sections in the  
388 -source.  
389 -<P>  
390 -  
391 -<H4>3.5 State Threads and NSPR</H4>  
392 -<P>  
393 -The State Threads library is a derivative of the Netscape Portable  
394 -Runtime library (NSPR) <A HREF=#refs7>[Reference 7]</A>. The primary goal of  
395 -NSPR is to provide a platform-independent layer for system facilities,  
396 -where system facilities include threads, thread synchronization, and I/O.  
397 -Performance and scalability are not the main concern of NSPR. The  
398 -State Threads library addresses performance and scalability while  
399 -remaining much smaller than NSPR. It is contained in 8 source files  
400 -as opposed to more than 400, but provides all the functionality that  
401 -is needed to write efficient IAs on UNIX-like platforms.  
402 -<P>  
403 -  
404 -<TABLE CELLPADDING=3>  
405 -<TR>  
406 -<TD></TD>  
407 -<TH>NSPR</TH>  
408 -<TH>State Threads</TH>  
409 -</TR>  
410 -<TR>  
411 -<TD><B>Lines of code</B></TD>  
412 -<TD ALIGN=RIGHT>~150,000</TD>  
413 -<TD ALIGN=RIGHT>~3000</TD>  
414 -</TR>  
415 -<TR>  
416 -<TD><B>Dynamic library size&nbsp;&nbsp;<BR>(debug version)</B></TD>  
417 -<TD></TD>  
418 -<TD></TD>  
419 -</TR>  
420 -<TR>  
421 -<TD>IRIX</TD>  
422 -<TD ALIGN=RIGHT>~700 KB</TD>  
423 -<TD ALIGN=RIGHT>~60 KB</TD>  
424 -</TR>  
425 -<TR>  
426 -<TD>Linux</TD>  
427 -<TD ALIGN=RIGHT>~900 KB</TD>  
428 -<TD ALIGN=RIGHT>~70 KB</TD>  
429 -</TR>  
430 -</TABLE>  
431 -<P>  
432 -  
433 -<H3>Conclusion</H3>  
434 -<P>  
435 -State Threads is an application library which provides a foundation for  
436 -writing <A HREF=#IA>Internet Applications</A>. To summarize, it has the  
437 -following <I>advantages</I>:  
438 -<P>  
439 -<UL>  
440 -<LI>It allows the design of fast and highly scalable applications. An  
441 -application will scale well with both load and number of CPUs.  
442 -<P>  
443 -<LI>It greatly simplifies application programming and debugging because, as a  
444 -rule, no mutual exclusion locking is necessary and the entire application is  
445 -free to use static variables and non-reentrant library functions.  
446 -</UL>  
447 -<P>  
448 -The library's main <I>limitation</I>:  
449 -<P>  
450 -<UL>  
451 -<LI>All I/O operations on sockets must use the State Thread library's I/O  
452 -functions because only those functions perform thread scheduling and prevent  
453 -the application's processes from blocking.  
454 -</UL>  
455 -<P>  
456 -  
457 -<H3>References</H3>  
458 -<OL>  
459 -<A NAME="refs1">  
460 -<LI> Apache Software Foundation,  
461 -<A HREF="http://www.apache.org">http://www.apache.org</A>.  
462 -<A NAME="refs2">  
463 -<LI> Douglas E. Comer, David L. Stevens, <I>Internetworking With TCP/IP,  
464 -Vol. III: Client-Server Programming And Applications</I>, Second Edition,  
465 -Ch. 8, 12.  
466 -<A NAME="refs3">  
467 -<LI> W. Richard Stevens, <I>UNIX Network Programming</I>, Second Edition,  
468 -Vol. 1, Ch. 15.  
469 -<A NAME="refs4">  
470 -<LI> Zeus Technology Limited,  
471 -<A HREF="http://www.zeus.co.uk/">http://www.zeus.co.uk</A>.  
472 -<A NAME="refs5">  
473 -<LI> Peter Druschel, Vivek S. Pai, Willy Zwaenepoel,  
474 -<A HREF="http://www.cs.rice.edu/~druschel/usenix99flash.ps.gz">  
475 -Flash: An Efficient and Portable Web Server</A>. In <I>Proceedings of the  
476 -USENIX 1999 Annual Technical Conference</I>, Monterey, CA, June 1999.  
477 -<A NAME="refs6">  
478 -<LI> GNU Portable Threads,  
479 -<A HREF="http://www.gnu.org/software/pth/">http://www.gnu.org/software/pth/</A>.  
480 -<A NAME="refs7">  
481 -<LI> Netscape Portable Runtime,  
482 -<A HREF="http://www.mozilla.org/docs/refList/refNSPR/">http://www.mozilla.org/docs/refList/refNSPR/</A>.  
483 -</OL>  
484 -  
485 -<H3>Other resources covering various architectural issues in IAs</H3>  
486 -<OL START=8>  
487 -<LI> Dan Kegel, <I>The C10K problem</I>,  
488 -<A HREF="http://www.kegel.com/c10k.html">http://www.kegel.com/c10k.html</A>.  
489 -</LI>  
490 -<LI> James C. Hu, Douglas C. Schmidt, Irfan Pyarali, <I>JAWS: Understanding  
491 -High Performance Web Systems</I>,  
492 -<A HREF="http://www.cs.wustl.edu/~jxh/research/research.html">http://www.cs.wustl.edu/~jxh/research/research.html</A>.</LI>  
493 -</OL>  
494 -<P>  
495 -<HR>  
496 -<P>  
497 -  
498 -<CENTER><FONT SIZE=-1>Portions created by SGI are Copyright &copy; 2000  
499 -Silicon Graphics, Inc. All rights reserved.</FONT></CENTER>  
500 -<P>  
501 -  
502 -</BODY>  
503 -</HTML>  
504 -  
1 -How the timeout heap works  
2 -  
3 -As of version 1.5, the State Threads Library represents the queue of  
4 -sleeping threads using a heap data structure rather than a sorted  
5 -linked list. This improves performance when there is a large number  
6 -of sleeping threads, since insertion into a heap takes O(log N) time  
7 -while insertion into a sorted list takes O(N) time. For example, in  
8 -one test 1000 threads were created, each thread called st_usleep()  
9 -with a random time interval, and then all the threads where  
10 -immediately interrupted and joined before the sleeps had a chance to  
11 -finish. The whole process was repeated 1000 times, for a total of a  
12 -million sleep queue insertions and removals. With the old list-based  
13 -sleep queue, this test took 100 seconds; now it takes only 12 seconds.  
14 -  
15 -Heap data structures are typically based on dynamically resized  
16 -arrays. However, since the existing ST code base was very nicely  
17 -structured around linking the thread objects into pointer-based lists  
18 -without the need for any auxiliary data structures, implementing the  
19 -heap using a similar nodes-and-pointers based approach seemed more  
20 -appropriate for ST than introducing a separate array.  
21 -  
22 -Thus, the new ST timeout heap works by organizing the existing  
23 -_st_thread_t objects in a balanced binary tree, just as they were  
24 -previously organized into a doubly-linked, sorted list. The global  
25 -_ST_SLEEPQ variable, formerly a linked list head, is now simply a  
26 -pointer to the root of this tree, and the root node of the tree is the  
27 -thread with the earliest timeout. Each thread object has two child  
28 -pointers, "left" and "right", pointing to threads with later timeouts.  
29 -  
30 -Each node in the tree is numbered with an integer index, corresponding  
31 -to the array index in an array-based heap, and the tree is kept fully  
32 -balanced and left-adjusted at all times. In other words, the tree  
33 -consists of any number of fully populated top levels, followed by a  
34 -single bottom level which may be partially populated, such that any  
35 -existing nodes form a contiguous block to the left and the spaces for  
36 -missing nodes form a contiguous block to the right. For example, if  
37 -there are nine threads waiting for a timeout, they are numbered and  
38 -arranged in a tree exactly as follows:  
39 -  
40 - 1  
41 - / \  
42 - 2 3  
43 - / \ / \  
44 - 4 5 6 7  
45 - / \  
46 - 8 9  
47 -  
48 -Each node has either no children, only a left child, or both a left  
49 -and a right child. Children always time out later than their parents  
50 -(this is called the "heap invariant"), but when a node has two  
51 -children, their mutual order is unspecified - the left child may time  
52 -out before or after the right child. If a node is numbered N, its  
53 -left child is numbered 2N, and its right child is numbered 2N+1.  
54 -  
55 -There is no pointer from a child to its parent; all pointers point  
56 -downward. Additions and deletions both work by starting at the root  
57 -and traversing the tree towards the leaves, going left or right  
58 -according to the binary digits forming the index of the destination  
59 -node. As nodes are added or deleted, existing nodes are rearranged to  
60 -maintain the heap invariant.  
1 -#  
2 -# Portions created by SGI are Copyright (C) 2000 Silicon Graphics, Inc.  
3 -# All Rights Reserved.  
4 -#  
5 -# Redistribution and use in source and binary forms, with or without  
6 -# modification, are permitted provided that the following conditions  
7 -# are met:  
8 -#  
9 -# 1. Redistributions of source code must retain the above copyright  
10 -# notice, this list of conditions and the following disclaimer.  
11 -# 2. Redistributions in binary form must reproduce the above copyright  
12 -# notice, this list of conditions and the following disclaimer in the  
13 -# documentation and/or other materials provided with the distribution.  
14 -# 3. Neither the name of Silicon Graphics, Inc. nor the names of its  
15 -# contributors may be used to endorse or promote products derived from  
16 -# this software without specific prior written permission.  
17 -#  
18 -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS  
19 -# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT  
20 -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR  
21 -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT  
22 -# HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,  
23 -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED  
24 -# TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR  
25 -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF  
26 -# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING  
27 -# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS  
28 -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  
29 -  
30 -##########################  
31 -# Supported OSes:  
32 -#  
33 -# AIX  
34 -# FREEBSD  
35 -# HPUX  
36 -# HPUX_64  
37 -# IRIX  
38 -# IRIX_64  
39 -# LINUX  
40 -# LINUX_IA64  
41 -# NETBSD  
42 -# OPENBSD  
43 -# OSF1  
44 -# SOLARIS  
45 -# SOLARIS_64  
46 -  
47 -##########################  
48 -  
49 -CC = cc  
50 -  
51 -SHELL = /bin/sh  
52 -ECHO = /bin/echo  
53 -  
54 -DEPTH = ..  
55 -BUILD =  
56 -TARGETDIR =  
57 -  
58 -DEFINES =  
59 -CFLAGS =  
60 -OTHER_FLAGS =  
61 -  
62 -OBJDIR = $(DEPTH)/$(TARGETDIR)  
63 -INCDIR = $(DEPTH)/$(TARGETDIR)  
64 -LIBST = $(OBJDIR)/libst.a  
65 -HEADER = $(INCDIR)/st.h  
66 -  
67 -LIBRESOLV =  
68 -EXTRALIBS =  
69 -  
70 -ifeq ($(OS),)  
71 -EXAMPLES = unknown  
72 -else  
73 -EXAMPLES = $(OBJDIR)/lookupdns $(OBJDIR)/proxy $(OBJDIR)/server  
74 -endif  
75 -  
76 -  
77 -##########################  
78 -# Platform section.  
79 -#  
80 -  
81 -ifeq (DARWIN, $(findstring DARWIN, $(OS)))  
82 -LIBRESOLV = -lresolv  
83 -endif  
84 -  
85 -ifeq (LINUX, $(findstring LINUX, $(OS)))  
86 -LIBRESOLV = -lresolv  
87 -endif  
88 -  
89 -ifeq (SOLARIS, $(findstring SOLARIS, $(OS)))  
90 -LIBRESOLV = -lresolv  
91 -EXTRALIBS = -lsocket -lnsl  
92 -endif  
93 -  
94 -#  
95 -# End of platform section.  
96 -##########################  
97 -  
98 -  
99 -all: $(EXAMPLES)  
100 -  
101 -$(OBJDIR)/lookupdns: lookupdns.c $(OBJDIR)/res.o $(LIBST) $(HEADER)  
102 - $(CC) $(CFLAGS) -I$(INCDIR) lookupdns.c $(OBJDIR)/res.o $(LIBST) $(LIBRESOLV) $(EXTRALIBS) -o $@  
103 -  
104 -$(OBJDIR)/proxy: proxy.c $(LIBST) $(HEADER)  
105 - $(CC) $(CFLAGS) -I$(INCDIR) proxy.c $(LIBST) $(EXTRALIBS) -o $@  
106 -  
107 -$(OBJDIR)/server: server.c $(OBJDIR)/error.o $(LIBST) $(HEADER)  
108 - $(CC) $(CFLAGS) -I$(INCDIR) server.c $(OBJDIR)/error.o $(LIBST) $(EXTRALIBS) -o $@  
109 -  
110 -$(OBJDIR)/%.o: %.c  
111 - $(CC) $(CFLAGS) -I$(INCDIR) -c $< -o $@  
112 -  
113 -.DEFAULT:  
114 - @cd $(DEPTH); $(MAKE) $@  
115 -  
1 -Portions created by SGI are Copyright (C) 2000 Silicon Graphics, Inc.  
2 -All Rights Reserved.  
3 -  
4 -  
5 -This directory contains three example programs.  
6 -  
7 -  
8 ----------------------------------------------------------------------------  
9 -  
10 -PROGRAM  
11 -  
12 - lookupdns  
13 -  
14 -FILES  
15 -  
16 - lookupdns.c  
17 - res.c  
18 -  
19 -USAGE  
20 -  
21 - lookupdns <hostname1> [<hostname2>] ...  
22 -  
23 -DESCRIPTION  
24 -  
25 - This program performs asynchronous DNS host name resolution and reports  
26 - IP address for each <hostname> specified as a command line argument.  
27 - One ST thread is created for each host name. All threads do host name  
28 - resolution concurrently.  
29 -  
30 -  
31 ----------------------------------------------------------------------------  
32 -  
33 -PROGRAM  
34 -  
35 - proxy  
36 -  
37 -FILES  
38 -  
39 - proxy.c  
40 -  
41 -USAGE  
42 -  
43 - proxy -l <local_addr> -r <remote_addr> [-p <num_processes>] [-S]  
44 -  
45 - -l <local_addr> bind to local address specified as [<host>]:<port>  
46 - -r <remote_addr> connect to remote address specified as <host>:<port>  
47 - -p <num_processes> create specified number of processes  
48 - -S serialize accept() calls from different processes  
49 - on the same listening socket (if needed).  
50 -  
51 -DESCRIPTION  
52 -  
53 - This program acts as a generic gateway. It listens for connections to a  
54 - local address. Upon accepting a client connection, it connects to the  
55 - specified remote address and then just pumps the data through without any  
56 - modification.  
57 -  
58 -  
59 ----------------------------------------------------------------------------  
60 -  
61 -PROGRAM  
62 -  
63 - server  
64 -  
65 -FILES  
66 -  
67 - server.c  
68 - error.c  
69 -  
70 -USAGE  
71 -  
72 - server -l <log_directory> [<options>]  
73 -  
74 - -l <log_directory> open all log files in specified directory.  
75 -  
76 - Possible options:  
77 -  
78 - -b <host>:<port> bind to specified address (multiple addresses  
79 - are permitted)  
80 - -p <num_processes> create specified number of processes  
81 - -t <min_thr>:<max_thr> specify thread limits per listening socket  
82 - across all processes  
83 - -u <user> change server's user id to specified value  
84 - -q <backlog> set max length of pending connections queue  
85 - -a enable access logging  
86 - -i run in interactive mode (useful for debugging)  
87 - -S serialize accept() calls from different processes  
88 - on the same listening socket (if needed).  
89 -  
90 -DESCRIPTION  
91 -  
92 - This program is a general server example. It accepts a client connection  
93 - and outputs a short HTML page. It can be easily adapted to provide  
94 - other services.  
95 -  
96 -  
97 ----------------------------------------------------------------------------  
98 -  
1 -/*  
2 - * Portions created by SGI are Copyright (C) 2000 Silicon Graphics, Inc.  
3 - * All Rights Reserved.  
4 - *  
5 - * Redistribution and use in source and binary forms, with or without  
6 - * modification, are permitted provided that the following conditions  
7 - * are met:  
8 - *  
9 - * 1. Redistributions of source code must retain the above copyright  
10 - * notice, this list of conditions and the following disclaimer.  
11 - * 2. Redistributions in binary form must reproduce the above copyright  
12 - * notice, this list of conditions and the following disclaimer in the  
13 - * documentation and/or other materials provided with the distribution.  
14 - * 3. Neither the name of Silicon Graphics, Inc. nor the names of its  
15 - * contributors may be used to endorse or promote products derived from  
16 - * this software without specific prior written permission.  
17 - *  
18 - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS  
19 - * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT  
20 - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR  
21 - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT  
22 - * HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,  
23 - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED  
24 - * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR  
25 - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF  
26 - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING  
27 - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS  
28 - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  
29 - */  
30 -  
31 -#include <stdarg.h>  
32 -#include <stdio.h>  
33 -#include <stdlib.h>  
34 -#include <string.h>  
35 -#include <unistd.h>  
36 -#include <errno.h>  
37 -#include "st.h"  
38 -  
39 -/*  
40 - * Simple error reporting functions.  
41 - * Suggested in W. Richard Stevens' "Advanced Programming in UNIX  
42 - * Environment".  
43 - */  
44 -  
45 -#define MAXLINE 4096 /* max line length */  
46 -  
47 -static void err_doit(int, int, const char *, va_list);  
48 -  
49 -  
50 -/*  
51 - * Nonfatal error related to a system call.  
52 - * Print a message and return.  
53 - */  
54 -void err_sys_report(int fd, const char *fmt, ...)  
55 -{  
56 - va_list ap;  
57 -  
58 - va_start(ap, fmt);  
59 - err_doit(fd, 1, fmt, ap);  
60 - va_end(ap);  
61 -}  
62 -  
63 -  
64 -/*  
65 - * Fatal error related to a system call.  
66 - * Print a message and terminate.  
67 - */  
68 -void err_sys_quit(int fd, const char *fmt, ...)  
69 -{  
70 - va_list ap;  
71 -  
72 - va_start(ap, fmt);  
73 - err_doit(fd, 1, fmt, ap);  
74 - va_end(ap);  
75 - exit(1);  
76 -}  
77 -  
78 -  
79 -/*  
80 - * Fatal error related to a system call.  
81 - * Print a message, dump core, and terminate.  
82 - */  
83 -void err_sys_dump(int fd, const char *fmt, ...)  
84 -{  
85 - va_list ap;  
86 -  
87 - va_start(ap, fmt);  
88 - err_doit(fd, 1, fmt, ap);  
89 - va_end(ap);  
90 - abort(); /* dump core and terminate */  
91 - exit(1); /* shouldn't get here */  
92 -}  
93 -  
94 -  
95 -/*  
96 - * Nonfatal error unrelated to a system call.  
97 - * Print a message and return.  
98 - */  
99 -void err_report(int fd, const char *fmt, ...)  
100 -{  
101 - va_list ap;  
102 -  
103 - va_start(ap, fmt);  
104 - err_doit(fd, 0, fmt, ap);  
105 - va_end(ap);  
106 -}  
107 -  
108 -  
109 -/*  
110 - * Fatal error unrelated to a system call.  
111 - * Print a message and terminate.  
112 - */  
113 -void err_quit(int fd, const char *fmt, ...)  
114 -{  
115 - va_list ap;  
116 -  
117 - va_start(ap, fmt);  
118 - err_doit(fd, 0, fmt, ap);  
119 - va_end(ap);  
120 - exit(1);  
121 -}  
122 -  
123 -  
124 -/*  
125 - * Return a pointer to a string containing current time.  
126 - */  
127 -char *err_tstamp(void)  
128 -{  
129 - static char *months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",  
130 - "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };  
131 - static char str[32];  
132 - static time_t lastt = 0;  
133 - struct tm *tmp;  
134 - time_t currt = st_time();  
135 -  
136 - if (currt == lastt)  
137 - return str;  
138 -  
139 - tmp = localtime(&currt);  
140 - sprintf(str, "[%02d/%s/%d:%02d:%02d:%02d] ", tmp->tm_mday,  
141 - months[tmp->tm_mon], 1900 + tmp->tm_year, tmp->tm_hour,  
142 - tmp->tm_min, tmp->tm_sec);  
143 - lastt = currt;  
144 -  
145 - return str;  
146 -}  
147 -  
148 -  
149 -/*  
150 - * Print a message and return to caller.  
151 - * Caller specifies "errnoflag".  
152 - */  
153 -static void err_doit(int fd, int errnoflag, const char *fmt, va_list ap)  
154 -{  
155 - int errno_save;  
156 - char buf[MAXLINE];  
157 -  
158 - errno_save = errno; /* value caller might want printed */  
159 - strcpy(buf, err_tstamp()); /* prepend a message with time stamp */  
160 - vsprintf(buf + strlen(buf), fmt, ap);  
161 - if (errnoflag)  
162 - sprintf(buf + strlen(buf), ": %s\n", strerror(errno_save));  
163 - else  
164 - strcat(buf, "\n");  
165 - write(fd, buf, strlen(buf));  
166 - errno = errno_save;  
167 -}  
168 -  
1 -/*  
2 - * Portions created by SGI are Copyright (C) 2000 Silicon Graphics, Inc.  
3 - * All Rights Reserved.  
4 - *  
5 - * Redistribution and use in source and binary forms, with or without  
6 - * modification, are permitted provided that the following conditions  
7 - * are met:  
8 - *  
9 - * 1. Redistributions of source code must retain the above copyright  
10 - * notice, this list of conditions and the following disclaimer.  
11 - * 2. Redistributions in binary form must reproduce the above copyright  
12 - * notice, this list of conditions and the following disclaimer in the  
13 - * documentation and/or other materials provided with the distribution.  
14 - * 3. Neither the name of Silicon Graphics, Inc. nor the names of its  
15 - * contributors may be used to endorse or promote products derived from  
16 - * this software without specific prior written permission.  
17 - *  
18 - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS  
19 - * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT  
20 - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR  
21 - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT  
22 - * HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,  
23 - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED  
24 - * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR  
25 - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF  
26 - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING  
27 - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS  
28 - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  
29 - */  
30 -  
31 -#include <stdio.h>  
32 -#include <stdlib.h>  
33 -#include <unistd.h>  
34 -#include <sys/time.h>  
35 -#include <sys/types.h>  
36 -#include <sys/socket.h>  
37 -#include <netinet/in.h>  
38 -#include <arpa/inet.h>  
39 -#include <netdb.h>  
40 -#include "st.h"  
41 -  
42 -#if !defined(NETDB_INTERNAL) && defined(h_NETDB_INTERNAL)  
43 -#define NETDB_INTERNAL h_NETDB_INTERNAL  
44 -#endif  
45 -  
46 -/* Resolution timeout (in microseconds) */  
47 -#define TIMEOUT (2*1000000LL)  
48 -  
49 -/* External function defined in the res.c file */  
50 -int dns_getaddr(const char *host, struct in_addr *addr, st_utime_t timeout);  
51 -  
52 -  
53 -void *do_resolve(void *host)  
54 -{  
55 - struct in_addr addr;  
56 -  
57 - /* Use dns_getaddr() instead of gethostbyname(3) to get IP address */  
58 - if (dns_getaddr(host, &addr, TIMEOUT) < 0) {  
59 - fprintf(stderr, "dns_getaddr: can't resolve %s: ", (char *)host);  
60 - if (h_errno == NETDB_INTERNAL)  
61 - perror("");  
62 - else  
63 - herror("");  
64 - } else  
65 - printf("%-40s %s\n", (char *)host, inet_ntoa(addr));  
66 -  
67 - return NULL;  
68 -}  
69 -  
70 -  
71 -/*  
72 - * Asynchronous DNS host name resolution. This program creates one  
73 - * ST thread for each host name (specified as command line arguments).  
74 - * All threads do host name resolution concurrently.  
75 - */  
76 -int main(int argc, char *argv[])  
77 -{  
78 - int i;  
79 -  
80 - if (argc < 2) {  
81 - fprintf(stderr, "Usage: %s <hostname1> [<hostname2>] ...\n", argv[0]);  
82 - exit(1);  
83 - }  
84 -  
85 - if (st_init() < 0) {  
86 - perror("st_init");  
87 - exit(1);  
88 - }  
89 -  
90 - for (i = 1; i < argc; i++) {  
91 - /* Create a separate thread for each host name */  
92 - if (st_thread_create(do_resolve, argv[i], 0, 0) == NULL) {  
93 - perror("st_thread_create");  
94 - exit(1);  
95 - }  
96 - }  
97 -  
98 - st_thread_exit(NULL);  
99 -  
100 - /* NOTREACHED */  
101 - return 1;  
102 -}  
103 -  
1 -/*  
2 - * Portions created by SGI are Copyright (C) 2000 Silicon Graphics, Inc.  
3 - * All Rights Reserved.  
4 - *  
5 - * Redistribution and use in source and binary forms, with or without  
6 - * modification, are permitted provided that the following conditions  
7 - * are met:  
8 - *  
9 - * 1. Redistributions of source code must retain the above copyright  
10 - * notice, this list of conditions and the following disclaimer.  
11 - * 2. Redistributions in binary form must reproduce the above copyright  
12 - * notice, this list of conditions and the following disclaimer in the  
13 - * documentation and/or other materials provided with the distribution.  
14 - * 3. Neither the name of Silicon Graphics, Inc. nor the names of its  
15 - * contributors may be used to endorse or promote products derived from  
16 - * this software without specific prior written permission.  
17 - *  
18 - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS  
19 - * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT  
20 - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR  
21 - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT  
22 - * HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,  
23 - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED  
24 - * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR  
25 - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF  
26 - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING  
27 - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS  
28 - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  
29 - */  
30 -  
31 -#include <stdio.h>  
32 -#include <stdlib.h>  
33 -#include <string.h>  
34 -#include <signal.h>  
35 -#include <unistd.h>  
36 -#include <fcntl.h>  
37 -#include <sys/types.h>  
38 -#include <sys/stat.h>  
39 -#include <sys/socket.h>  
40 -#include <netinet/in.h>  
41 -#include <arpa/inet.h>  
42 -#include <netdb.h>  
43 -#include "st.h"  
44 -  
45 -#define IOBUFSIZE (16*1024)  
46 -  
47 -#define IOV_LEN 256  
48 -#define IOV_COUNT (IOBUFSIZE / IOV_LEN)  
49 -  
50 -#ifndef INADDR_NONE  
51 -#define INADDR_NONE 0xffffffff  
52 -#endif  
53 -  
54 -static char *prog; /* Program name */  
55 -static struct sockaddr_in rmt_addr; /* Remote address */  
56 -  
57 -static unsigned long testing;  
58 -#define TESTING_VERBOSE 0x1  
59 -#define TESTING_READV 0x2  
60 -#define TESTING_READ_RESID 0x4  
61 -#define TESTING_WRITEV 0x8  
62 -#define TESTING_WRITE_RESID 0x10  
63 -  
64 -static void read_address(const char *str, struct sockaddr_in *sin);  
65 -static void start_daemon(void);  
66 -static int cpu_count(void);  
67 -static void set_concurrency(int nproc);  
68 -static void *handle_request(void *arg);  
69 -static void print_sys_error(const char *msg);  
70 -  
71 -  
72 -/*  
73 - * This program acts as a generic gateway. It listens for connections  
74 - * to a local address ('-l' option). Upon accepting a client connection,  
75 - * it connects to the specified remote address ('-r' option) and then  
76 - * just pumps the data through without any modification.  
77 - */  
78 -int main(int argc, char *argv[])  
79 -{  
80 - extern char *optarg;  
81 - int opt, sock, n;  
82 - int laddr, raddr, num_procs, alt_ev, one_process;  
83 - int serialize_accept = 0;  
84 - struct sockaddr_in lcl_addr, cli_addr;  
85 - st_netfd_t cli_nfd, srv_nfd;  
86 -  
87 - prog = argv[0];  
88 - num_procs = laddr = raddr = alt_ev = one_process = 0;  
89 -  
90 - /* Parse arguments */  
91 - while((opt = getopt(argc, argv, "l:r:p:Saht:X")) != EOF) {  
92 - switch (opt) {  
93 - case 'a':  
94 - alt_ev = 1;  
95 - break;  
96 - case 'l':  
97 - read_address(optarg, &lcl_addr);  
98 - laddr = 1;  
99 - break;  
100 - case 'r':  
101 - read_address(optarg, &rmt_addr);  
102 - if (rmt_addr.sin_addr.s_addr == INADDR_ANY) {  
103 - fprintf(stderr, "%s: invalid remote address: %s\n", prog, optarg);  
104 - exit(1);  
105 - }  
106 - raddr = 1;  
107 - break;  
108 - case 'p':  
109 - num_procs = atoi(optarg);  
110 - if (num_procs < 1) {  
111 - fprintf(stderr, "%s: invalid number of processes: %s\n", prog, optarg);  
112 - exit(1);  
113 - }  
114 - break;  
115 - case 'S':  
116 - /*  
117 - * Serialization decision is tricky on some platforms. For example,  
118 - * Solaris 2.6 and above has kernel sockets implementation, so supposedly  
119 - * there is no need for serialization. The ST library may be compiled  
120 - * on one OS version, but used on another, so the need for serialization  
121 - * should be determined at run time by the application. Since it's just  
122 - * an example, the serialization decision is left up to user.  
123 - * Only on platforms where the serialization is never needed on any OS  
124 - * version st_netfd_serialize_accept() is a no-op.  
125 - */  
126 - serialize_accept = 1;  
127 - break;  
128 - case 't':  
129 - testing = strtoul(optarg, NULL, 0);  
130 - break;  
131 - case 'X':  
132 - one_process = 1;  
133 - break;  
134 - case 'h':  
135 - case '?':  
136 - fprintf(stderr, "Usage: %s [options] -l <[host]:port> -r <host:port>\n",  
137 - prog);  
138 - fprintf(stderr, "options are:\n");  
139 - fprintf(stderr, " -p <num_processes> number of parallel processes\n");  
140 - fprintf(stderr, " -S serialize accepts\n");  
141 - fprintf(stderr, " -a use alternate event system\n");  
142 -#ifdef DEBUG  
143 - fprintf(stderr, " -t mask testing/debugging mode\n");  
144 - fprintf(stderr, " -X one process, don't daemonize\n");  
145 -#endif  
146 - exit(1);  
147 - }  
148 - }  
149 - if (!laddr) {  
150 - fprintf(stderr, "%s: local address required\n", prog);  
151 - exit(1);  
152 - }  
153 - if (!raddr) {  
154 - fprintf(stderr, "%s: remote address required\n", prog);  
155 - exit(1);  
156 - }  
157 - if (num_procs == 0)  
158 - num_procs = cpu_count();  
159 -  
160 - fprintf(stderr, "%s: starting proxy daemon on %s:%d\n", prog,  
161 - inet_ntoa(lcl_addr.sin_addr), ntohs(lcl_addr.sin_port));  
162 -  
163 - /* Start the daemon */  
164 - if (one_process)  
165 - num_procs = 1;  
166 - else  
167 - start_daemon();  
168 -  
169 - if (alt_ev)  
170 - st_set_eventsys(ST_EVENTSYS_ALT);  
171 -  
172 - /* Initialize the ST library */  
173 - if (st_init() < 0) {  
174 - print_sys_error("st_init");  
175 - exit(1);  
176 - }  
177 -  
178 - /* Create and bind listening socket */  
179 - if ((sock = socket(PF_INET, SOCK_STREAM, 0)) < 0) {  
180 - print_sys_error("socket");  
181 - exit(1);  
182 - }  
183 - n = 1;  
184 - if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&n, sizeof(n)) < 0) {  
185 - print_sys_error("setsockopt");  
186 - exit(1);  
187 - }  
188 - if (bind(sock, (struct sockaddr *)&lcl_addr, sizeof(lcl_addr)) < 0) {  
189 - print_sys_error("bind");  
190 - exit(1);  
191 - }  
192 - listen(sock, 128);  
193 - if ((srv_nfd = st_netfd_open_socket(sock)) == NULL) {  
194 - print_sys_error("st_netfd_open");  
195 - exit(1);  
196 - }  
197 - /* See the comment regarding serialization decision above */  
198 - if (num_procs > 1 && serialize_accept && st_netfd_serialize_accept(srv_nfd)  
199 - < 0) {  
200 - print_sys_error("st_netfd_serialize_accept");  
201 - exit(1);  
202 - }  
203 -  
204 - /* Start server processes */  
205 - if (!one_process)  
206 - set_concurrency(num_procs);  
207 -  
208 - for ( ; ; ) {  
209 - n = sizeof(cli_addr);  
210 - cli_nfd = st_accept(srv_nfd, (struct sockaddr *)&cli_addr, &n,  
211 - ST_UTIME_NO_TIMEOUT);  
212 - if (cli_nfd == NULL) {  
213 - print_sys_error("st_accept");  
214 - exit(1);  
215 - }  
216 - if (st_thread_create(handle_request, cli_nfd, 0, 0) == NULL) {  
217 - print_sys_error("st_thread_create");  
218 - exit(1);  
219 - }  
220 - }  
221 -  
222 - /* NOTREACHED */  
223 - return 1;  
224 -}  
225 -  
226 -  
227 -static void read_address(const char *str, struct sockaddr_in *sin)  
228 -{  
229 - char host[128], *p;  
230 - struct hostent *hp;  
231 - unsigned short port;  
232 -  
233 - strcpy(host, str);  
234 - if ((p = strchr(host, ':')) == NULL) {  
235 - fprintf(stderr, "%s: invalid address: %s\n", prog, host);  
236 - exit(1);  
237 - }  
238 - *p++ = '\0';  
239 - port = (unsigned short) atoi(p);  
240 - if (port < 1) {  
241 - fprintf(stderr, "%s: invalid port: %s\n", prog, p);  
242 - exit(1);  
243 - }  
244 -  
245 - memset(sin, 0, sizeof(struct sockaddr_in));  
246 - sin->sin_family = AF_INET;  
247 - sin->sin_port = htons(port);  
248 - if (host[0] == '\0') {  
249 - sin->sin_addr.s_addr = INADDR_ANY;  
250 - return;  
251 - }  
252 - sin->sin_addr.s_addr = inet_addr(host);  
253 - if (sin->sin_addr.s_addr == INADDR_NONE) {  
254 - /* not dotted-decimal */  
255 - if ((hp = gethostbyname(host)) == NULL) {  
256 - fprintf(stderr, "%s: can't resolve address: %s\n", prog, host);  
257 - exit(1);  
258 - }  
259 - memcpy(&sin->sin_addr, hp->h_addr, hp->h_length);  
260 - }  
261 -}  
262 -  
263 -#ifdef DEBUG  
264 -static void show_iov(const struct iovec *iov, int niov)  
265 -{  
266 - int i;  
267 - size_t total;  
268 -  
269 - printf("iov %p has %d entries:\n", iov, niov);  
270 - total = 0;  
271 - for (i = 0; i < niov; i++) {  
272 - printf("iov[%3d] iov_base=%p iov_len=0x%lx(%lu)\n",  
273 - i, iov[i].iov_base, (unsigned long) iov[i].iov_len,  
274 - (unsigned long) iov[i].iov_len);  
275 - total += iov[i].iov_len;  
276 - }  
277 - printf("total 0x%lx(%ld)\n", (unsigned long) total, (unsigned long) total);  
278 -}  
279 -  
280 -/*  
281 - * This version is tricked out to test all the  
282 - * st_(read|write)v?(_resid)? variants. Use the non-DEBUG version for  
283 - * anything serious. st_(read|write) are all this function really  
284 - * needs.  
285 - */  
286 -static int pass(st_netfd_t in, st_netfd_t out)  
287 -{  
288 - char buf[IOBUFSIZE];  
289 - struct iovec iov[IOV_COUNT];  
290 - int ioviter, nw, nr;  
291 -  
292 - if (testing & TESTING_READV) {  
293 - for (ioviter = 0; ioviter < IOV_COUNT; ioviter++) {  
294 - iov[ioviter].iov_base = &buf[ioviter * IOV_LEN];  
295 - iov[ioviter].iov_len = IOV_LEN;  
296 - }  
297 - if (testing & TESTING_VERBOSE) {  
298 - printf("readv(%p)...\n", in);  
299 - show_iov(iov, IOV_COUNT);  
300 - }  
301 - if (testing & TESTING_READ_RESID) {  
302 - struct iovec *riov = iov;  
303 - int riov_cnt = IOV_COUNT;  
304 - if (st_readv_resid(in, &riov, &riov_cnt, ST_UTIME_NO_TIMEOUT) == 0) {  
305 - if (testing & TESTING_VERBOSE) {  
306 - printf("resid\n");  
307 - show_iov(riov, riov_cnt);  
308 - printf("full\n");  
309 - show_iov(iov, IOV_COUNT);  
310 - }  
311 - nr = 0;  
312 - for (ioviter = 0; ioviter < IOV_COUNT; ioviter++)  
313 - nr += iov[ioviter].iov_len;  
314 - nr = IOBUFSIZE - nr;  
315 - } else  
316 - nr = -1;  
317 - } else  
318 - nr = (int) st_readv(in, iov, IOV_COUNT, ST_UTIME_NO_TIMEOUT);  
319 - } else {  
320 - if (testing & TESTING_READ_RESID) {  
321 - size_t resid = IOBUFSIZE;  
322 - if (st_read_resid(in, buf, &resid, ST_UTIME_NO_TIMEOUT) == 0)  
323 - nr = IOBUFSIZE - resid;  
324 - else  
325 - nr = -1;  
326 - } else  
327 - nr = (int) st_read(in, buf, IOBUFSIZE, ST_UTIME_NO_TIMEOUT);  
328 - }  
329 - if (testing & TESTING_VERBOSE)  
330 - printf("got 0x%x(%d) E=%d\n", nr, nr, errno);  
331 -  
332 - if (nr <= 0)  
333 - return 0;  
334 -  
335 - if (testing & TESTING_WRITEV) {  
336 - for (nw = 0, ioviter = 0; nw < nr;  
337 - nw += iov[ioviter].iov_len, ioviter++) {  
338 - iov[ioviter].iov_base = &buf[nw];  
339 - iov[ioviter].iov_len = nr - nw;  
340 - if (iov[ioviter].iov_len > IOV_LEN)  
341 - iov[ioviter].iov_len = IOV_LEN;  
342 - }  
343 - if (testing & TESTING_VERBOSE) {  
344 - printf("writev(%p)...\n", out);  
345 - show_iov(iov, ioviter);  
346 - }  
347 - if (testing & TESTING_WRITE_RESID) {  
348 - struct iovec *riov = iov;  
349 - int riov_cnt = ioviter;  
350 - if (st_writev_resid(out, &riov, &riov_cnt, ST_UTIME_NO_TIMEOUT) == 0) {  
351 - if (testing & TESTING_VERBOSE) {  
352 - printf("resid\n");  
353 - show_iov(riov, riov_cnt);  
354 - printf("full\n");  
355 - show_iov(iov, ioviter);  
356 - }  
357 - nw = 0;  
358 - while (--ioviter >= 0)  
359 - nw += iov[ioviter].iov_len;  
360 - nw = nr - nw;  
361 - } else  
362 - nw = -1;  
363 - } else  
364 - nw = st_writev(out, iov, ioviter, ST_UTIME_NO_TIMEOUT);  
365 - } else {  
366 - if (testing & TESTING_WRITE_RESID) {  
367 - size_t resid = nr;  
368 - if (st_write_resid(out, buf, &resid, ST_UTIME_NO_TIMEOUT) == 0)  
369 - nw = nr - resid;  
370 - else  
371 - nw = -1;  
372 - } else  
373 - nw = st_write(out, buf, nr, ST_UTIME_NO_TIMEOUT);  
374 - }  
375 - if (testing & TESTING_VERBOSE)  
376 - printf("put 0x%x(%d) E=%d\n", nw, nw, errno);  
377 -  
378 - if (nw != nr)  
379 - return 0;  
380 -  
381 - return 1;  
382 -}  
383 -#else /* DEBUG */  
384 -/*  
385 - * This version is the simple one suitable for serious use.  
386 - */  
387 -static int pass(st_netfd_t in, st_netfd_t out)  
388 -{  
389 - char buf[IOBUFSIZE];  
390 - int nw, nr;  
391 -  
392 - nr = (int) st_read(in, buf, IOBUFSIZE, ST_UTIME_NO_TIMEOUT);  
393 - if (nr <= 0)  
394 - return 0;  
395 -  
396 - nw = st_write(out, buf, nr, ST_UTIME_NO_TIMEOUT);  
397 - if (nw != nr)  
398 - return 0;  
399 -  
400 - return 1;  
401 -}  
402 -#endif  
403 -  
404 -static void *handle_request(void *arg)  
405 -{  
406 - struct pollfd pds[2];  
407 - st_netfd_t cli_nfd, rmt_nfd;  
408 - int sock;  
409 -  
410 - cli_nfd = (st_netfd_t) arg;  
411 - pds[0].fd = st_netfd_fileno(cli_nfd);  
412 - pds[0].events = POLLIN;  
413 -  
414 - /* Connect to remote host */  
415 - if ((sock = socket(PF_INET, SOCK_STREAM, 0)) < 0) {  
416 - print_sys_error("socket");  
417 - goto done;  
418 - }  
419 - if ((rmt_nfd = st_netfd_open_socket(sock)) == NULL) {  
420 - print_sys_error("st_netfd_open_socket");  
421 - close(sock);  
422 - goto done;  
423 - }  
424 - if (st_connect(rmt_nfd, (struct sockaddr *)&rmt_addr,  
425 - sizeof(rmt_addr), ST_UTIME_NO_TIMEOUT) < 0) {  
426 - print_sys_error("st_connect");  
427 - st_netfd_close(rmt_nfd);  
428 - goto done;  
429 - }  
430 - pds[1].fd = sock;  
431 - pds[1].events = POLLIN;  
432 -  
433 - /*  
434 - * Now just pump the data through.  
435 - * XXX This should use one thread for each direction for true full-duplex.  
436 - */  
437 - for ( ; ; ) {  
438 - pds[0].revents = 0;  
439 - pds[1].revents = 0;  
440 -  
441 - if (st_poll(pds, 2, ST_UTIME_NO_TIMEOUT) <= 0) {  
442 - print_sys_error("st_poll");  
443 - break;  
444 - }  
445 -  
446 - if (pds[0].revents & POLLIN) {  
447 - if (!pass(cli_nfd, rmt_nfd))  
448 - break;  
449 - }  
450 -  
451 - if (pds[1].revents & POLLIN) {  
452 - if (!pass(rmt_nfd, cli_nfd))  
453 - break;  
454 - }  
455 - }  
456 - st_netfd_close(rmt_nfd);  
457 -  
458 -done:  
459 -  
460 - st_netfd_close(cli_nfd);  
461 -  
462 - return NULL;  
463 -}  
464 -  
465 -static void start_daemon(void)  
466 -{  
467 - pid_t pid;  
468 -  
469 - /* Start forking */  
470 - if ((pid = fork()) < 0) {  
471 - print_sys_error("fork");  
472 - exit(1);  
473 - }  
474 - if (pid > 0)  
475 - exit(0); /* parent */  
476 -  
477 - /* First child process */  
478 - setsid(); /* become session leader */  
479 -  
480 - if ((pid = fork()) < 0) {  
481 - print_sys_error("fork");  
482 - exit(1);  
483 - }  
484 - if (pid > 0) /* first child */  
485 - exit(0);  
486 -  
487 - chdir("/");  
488 - umask(022);  
489 -}  
490 -  
491 -/*  
492 - * Create separate processes ("virtual processors"). Since it's just an  
493 - * example, there is no watchdog - the parent just exits leaving children  
494 - * on their own.  
495 - */  
496 -static void set_concurrency(int nproc)  
497 -{  
498 - pid_t pid;  
499 - int i;  
500 -  
501 - if (nproc < 1)  
502 - nproc = 1;  
503 -  
504 - for (i = 0; i < nproc; i++) {  
505 - if ((pid = fork()) < 0) {  
506 - print_sys_error("fork");  
507 - exit(1);  
508 - }  
509 - /* Child returns */  
510 - if (pid == 0)  
511 - return;  
512 - }  
513 -  
514 - /* Parent just exits */  
515 - exit(0);  
516 -}  
517 -  
518 -static int cpu_count(void)  
519 -{  
520 - int n;  
521 -  
522 -#if defined (_SC_NPROCESSORS_ONLN)  
523 - n = (int) sysconf(_SC_NPROCESSORS_ONLN);  
524 -#elif defined (_SC_NPROC_ONLN)  
525 - n = (int) sysconf(_SC_NPROC_ONLN);  
526 -#elif defined (HPUX)  
527 -#include <sys/mpctl.h>  
528 - n = mpctl(MPC_GETNUMSPUS, 0, 0);  
529 -#else  
530 - n = -1;  
531 - errno = ENOSYS;  
532 -#endif  
533 -  
534 - return n;  
535 -}  
536 -  
537 -static void print_sys_error(const char *msg)  
538 -{  
539 - fprintf(stderr, "%s: %s: %s\n", prog, msg, strerror(errno));  
540 -}  
541 -  
1 -/*  
2 - * Copyright (c) 1985, 1988, 1993  
3 - * The Regents of the University of California. All rights reserved.  
4 - *  
5 - * Redistribution and use in source and binary forms, with or without  
6 - * modification, are permitted provided that the following conditions  
7 - * are met:  
8 - * 1. Redistributions of source code must retain the above copyright  
9 - * notice, this list of conditions and the following disclaimer.  
10 - * 2. Redistributions in binary form must reproduce the above copyright  
11 - * notice, this list of conditions and the following disclaimer in the  
12 - * documentation and/or other materials provided with the distribution.  
13 - * 3. All advertising materials mentioning features or use of this software  
14 - * must display the following acknowledgement:  
15 - * This product includes software developed by the University of  
16 - * California, Berkeley and its contributors.  
17 - * 4. Neither the name of the University nor the names of its contributors  
18 - * may be used to endorse or promote products derived from this software  
19 - * without specific prior written permission.  
20 - *  
21 - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND  
22 - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE  
23 - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE  
24 - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE  
25 - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL  
26 - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS  
27 - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)  
28 - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT  
29 - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY  
30 - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF  
31 - * SUCH DAMAGE.  
32 - *  
33 - * Portions created by SGI are Copyright (C) 2000 Silicon Graphics, Inc.  
34 - * All Rights Reserved.  
35 - *  
36 - * Redistribution and use in source and binary forms, with or without  
37 - * modification, are permitted provided that the following conditions  
38 - * are met:  
39 - *  
40 - * 1. Redistributions of source code must retain the above copyright  
41 - * notice, this list of conditions and the following disclaimer.  
42 - * 2. Redistributions in binary form must reproduce the above copyright  
43 - * notice, this list of conditions and the following disclaimer in the  
44 - * documentation and/or other materials provided with the distribution.  
45 - * 3. Neither the name of Silicon Graphics, Inc. nor the names of its  
46 - * contributors may be used to endorse or promote products derived from  
47 - * this software without specific prior written permission.  
48 - *  
49 - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS  
50 - * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT  
51 - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR  
52 - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT  
53 - * HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,  
54 - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED  
55 - * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR  
56 - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF  
57 - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING  
58 - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS  
59 - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  
60 - */  
61 -  
62 -#if defined (DARWIN)  
63 -#define BIND_8_COMPAT  
64 -#endif  
65 -  
66 -#include <stdio.h>  
67 -#include <string.h>  
68 -#include <unistd.h>  
69 -#include <sys/types.h>  
70 -#include <sys/socket.h>  
71 -#include <netinet/in.h>  
72 -#include <arpa/nameser.h>  
73 -#include <resolv.h>  
74 -#include <netdb.h>  
75 -#include <errno.h>  
76 -#include "st.h"  
77 -  
78 -#define MAXPACKET 1024  
79 -  
80 -#if !defined(NETDB_INTERNAL) && defined(h_NETDB_INTERNAL)  
81 -#define NETDB_INTERNAL h_NETDB_INTERNAL  
82 -#endif  
83 -  
84 -/* New in Solaris 7 */  
85 -#if !defined(_getshort) && defined(ns_get16)  
86 -#define _getshort(cp) ns_get16(cp)  
87 -#endif  
88 -  
89 -typedef union {  
90 - HEADER hdr;  
91 - u_char buf[MAXPACKET];  
92 -} querybuf_t;  
93 -  
94 -  
95 -static int parse_answer(querybuf_t *ans, int len, struct in_addr *addr)  
96 -{  
97 - char buf[MAXPACKET];  
98 - HEADER *ahp;  
99 - u_char *cp, *eoa;  
100 - int type, n;  
101 -  
102 - ahp = &ans->hdr;  
103 - eoa = ans->buf + len;  
104 - cp = ans->buf + sizeof(HEADER);  
105 -  
106 - while (ahp->qdcount > 0) {  
107 - ahp->qdcount--;  
108 - cp += dn_skipname(cp, eoa) + QFIXEDSZ;  
109 - }  
110 - while (ahp->ancount > 0 && cp < eoa) {  
111 - ahp->ancount--;  
112 - if ((n = dn_expand(ans->buf, eoa, cp, buf, sizeof(buf))) < 0)  
113 - break;  
114 - cp += n;  
115 - type = _getshort(cp);  
116 - cp += 8;  
117 - n = _getshort(cp);  
118 - cp += 2;  
119 - if (type == T_CNAME) {  
120 - cp += n;  
121 - continue;  
122 - }  
123 - memcpy(addr, cp, n);  
124 - return 0;  
125 - }  
126 -  
127 - h_errno = TRY_AGAIN;  
128 - return -1;  
129 -}  
130 -  
131 -  
132 -static int query_domain(st_netfd_t nfd, const char *name, struct in_addr *addr,  
133 - st_utime_t timeout)  
134 -{  
135 - querybuf_t qbuf;  
136 - u_char *buf = qbuf.buf;  
137 - HEADER *hp = &qbuf.hdr;  
138 - int blen = sizeof(qbuf);  
139 - int i, len, id;  
140 -  
141 - for (i = 0; i < _res.nscount; i++) {  
142 - len = res_mkquery(QUERY, name, C_IN, T_A, NULL, 0, NULL, buf, blen);  
143 - if (len <= 0) {  
144 - h_errno = NO_RECOVERY;  
145 - return -1;  
146 - }  
147 - id = hp->id;  
148 -  
149 - if (st_sendto(nfd, buf, len, (struct sockaddr *)&(_res.nsaddr_list[i]),  
150 - sizeof(struct sockaddr), timeout) != len) {  
151 - h_errno = NETDB_INTERNAL;  
152 - /* EINTR means interrupt by other thread, NOT by a caught signal */  
153 - if (errno == EINTR)  
154 - return -1;  
155 - continue;  
156 - }  
157 -  
158 - /* Wait for reply */  
159 - do {  
160 - len = st_recvfrom(nfd, buf, blen, NULL, NULL, timeout);  
161 - if (len <= 0)  
162 - break;  
163 - } while (id != hp->id);  
164 -  
165 - if (len < HFIXEDSZ) {  
166 - h_errno = NETDB_INTERNAL;  
167 - if (len >= 0)  
168 - errno = EMSGSIZE;  
169 - else if (errno == EINTR) /* see the comment above */  
170 - return -1;  
171 - continue;  
172 - }  
173 -  
174 - hp->ancount = ntohs(hp->ancount);  
175 - hp->qdcount = ntohs(hp->qdcount);  
176 - if ((hp->rcode != NOERROR) || (hp->ancount == 0)) {  
177 - switch (hp->rcode) {  
178 - case NXDOMAIN:  
179 - h_errno = HOST_NOT_FOUND;  
180 - break;  
181 - case SERVFAIL:  
182 - h_errno = TRY_AGAIN;  
183 - break;  
184 - case NOERROR:  
185 - h_errno = NO_DATA;  
186 - break;  
187 - case FORMERR:  
188 - case NOTIMP:  
189 - case REFUSED:  
190 - default:  
191 - h_errno = NO_RECOVERY;  
192 - }  
193 - continue;  
194 - }  
195 -  
196 - if (parse_answer(&qbuf, len, addr) == 0)  
197 - return 0;  
198 - }  
199 -  
200 - return -1;  
201 -}  
202 -  
203 -  
204 -#define CLOSE_AND_RETURN(ret) \  
205 - { \  
206 - n = errno; \  
207 - st_netfd_close(nfd); \  
208 - errno = n; \  
209 - return (ret); \  
210 - }  
211 -  
212 -  
213 -int dns_getaddr(const char *host, struct in_addr *addr, st_utime_t timeout)  
214 -{  
215 - char name[MAXDNAME], **domain;  
216 - const char *cp;  
217 - int s, n, maxlen, dots;  
218 - int trailing_dot, tried_as_is;  
219 - st_netfd_t nfd;  
220 -  
221 - if ((_res.options & RES_INIT) == 0 && res_init() == -1) {  
222 - h_errno = NETDB_INTERNAL;  
223 - return -1;  
224 - }  
225 - if (_res.options & RES_USEVC) {  
226 - h_errno = NETDB_INTERNAL;  
227 - errno = ENOSYS;  
228 - return -1;  
229 - }  
230 - if (!host || *host == '\0') {  
231 - h_errno = HOST_NOT_FOUND;  
232 - return -1;  
233 - }  
234 -  
235 - /* Create UDP socket */  
236 - if ((s = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {  
237 - h_errno = NETDB_INTERNAL;  
238 - return -1;  
239 - }  
240 - if ((nfd = st_netfd_open_socket(s)) == NULL) {  
241 - h_errno = NETDB_INTERNAL;  
242 - n = errno;  
243 - close(s);  
244 - errno = n;  
245 - return -1;  
246 - }  
247 -  
248 - maxlen = sizeof(name) - 1;  
249 - n = 0;  
250 - dots = 0;  
251 - trailing_dot = 0;  
252 - tried_as_is = 0;  
253 -  
254 - for (cp = host; *cp && n < maxlen; cp++) {  
255 - dots += (*cp == '.');  
256 - name[n++] = *cp;  
257 - }  
258 - if (name[n - 1] == '.')  
259 - trailing_dot = 1;  
260 -  
261 - /*  
262 - * If there are dots in the name already, let's just give it a try  
263 - * 'as is'. The threshold can be set with the "ndots" option.  
264 - */  
265 - if (dots >= _res.ndots) {  
266 - if (query_domain(nfd, host, addr, timeout) == 0)  
267 - CLOSE_AND_RETURN(0);  
268 - if (h_errno == NETDB_INTERNAL && errno == EINTR)  
269 - CLOSE_AND_RETURN(-1);  
270 - tried_as_is = 1;  
271 - }  
272 -  
273 - /*  
274 - * We do at least one level of search if  
275 - * - there is no dot and RES_DEFNAME is set, or  
276 - * - there is at least one dot, there is no trailing dot,  
277 - * and RES_DNSRCH is set.  
278 - */  
279 - if ((!dots && (_res.options & RES_DEFNAMES)) ||  
280 - (dots && !trailing_dot && (_res.options & RES_DNSRCH))) {  
281 - name[n++] = '.';  
282 - for (domain = _res.dnsrch; *domain; domain++) {  
283 - strncpy(name + n, *domain, maxlen - n);  
284 - if (query_domain(nfd, name, addr, timeout) == 0)  
285 - CLOSE_AND_RETURN(0);  
286 - if (h_errno == NETDB_INTERNAL && errno == EINTR)  
287 - CLOSE_AND_RETURN(-1);  
288 - if (!(_res.options & RES_DNSRCH))  
289 - break;  
290 - }  
291 - }  
292 -  
293 - /*  
294 - * If we have not already tried the name "as is", do that now.  
295 - * note that we do this regardless of how many dots were in the  
296 - * name or whether it ends with a dot.  
297 - */  
298 - if (!tried_as_is) {  
299 - if (query_domain(nfd, host, addr, timeout) == 0)  
300 - CLOSE_AND_RETURN(0);  
301 - }  
302 -  
303 - CLOSE_AND_RETURN(-1);  
304 -}  
305 -  
1 -/*  
2 - * Portions created by SGI are Copyright (C) 2000 Silicon Graphics, Inc.  
3 - * All Rights Reserved.  
4 - *  
5 - * Redistribution and use in source and binary forms, with or without  
6 - * modification, are permitted provided that the following conditions  
7 - * are met:  
8 - *  
9 - * 1. Redistributions of source code must retain the above copyright  
10 - * notice, this list of conditions and the following disclaimer.  
11 - * 2. Redistributions in binary form must reproduce the above copyright  
12 - * notice, this list of conditions and the following disclaimer in the  
13 - * documentation and/or other materials provided with the distribution.  
14 - * 3. Neither the name of Silicon Graphics, Inc. nor the names of its  
15 - * contributors may be used to endorse or promote products derived from  
16 - * this software without specific prior written permission.  
17 - *  
18 - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS  
19 - * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT  
20 - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR  
21 - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT  
22 - * HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,  
23 - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED  
24 - * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR  
25 - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF  
26 - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING  
27 - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS  
28 - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  
29 - */  
30 -  
31 -#include <stdio.h>  
32 -#include <stdlib.h>  
33 -#include <string.h>  
34 -#include <unistd.h>  
35 -#include <time.h>  
36 -#include <sys/types.h>  
37 -#include <sys/stat.h>  
38 -#include <sys/socket.h>  
39 -#include <sys/wait.h>  
40 -#include <netinet/in.h>  
41 -#include <arpa/inet.h>  
42 -#include <netdb.h>  
43 -#include <fcntl.h>  
44 -#include <signal.h>  
45 -#include <pwd.h>  
46 -#include "st.h"  
47 -  
48 -  
49 -/******************************************************************  
50 - * Server configuration parameters  
51 - */  
52 -  
53 -/* Log files */  
54 -#define PID_FILE "pid"  
55 -#define ERRORS_FILE "errors"  
56 -#define ACCESS_FILE "access"  
57 -  
58 -/* Default server port */  
59 -#define SERV_PORT_DEFAULT 8000  
60 -  
61 -/* Socket listen queue size */  
62 -#define LISTENQ_SIZE_DEFAULT 256  
63 -  
64 -/* Max number of listening sockets ("hardware virtual servers") */  
65 -#define MAX_BIND_ADDRS 16  
66 -  
67 -/* Max number of "spare" threads per process per socket */  
68 -#define MAX_WAIT_THREADS_DEFAULT 8  
69 -  
70 -/* Number of file descriptors needed to handle one client session */  
71 -#define FD_PER_THREAD 2  
72 -  
73 -/* Access log buffer flushing interval (in seconds) */  
74 -#define ACCLOG_FLUSH_INTERVAL 30  
75 -  
76 -/* Request read timeout (in seconds) */  
77 -#define REQUEST_TIMEOUT 30  
78 -  
79 -  
80 -/******************************************************************  
81 - * Global data  
82 - */  
83 -  
84 -struct socket_info {  
85 - st_netfd_t nfd; /* Listening socket */  
86 - char *addr; /* Bind address */  
87 - unsigned int port; /* Port */  
88 - int wait_threads; /* Number of threads waiting to accept */  
89 - int busy_threads; /* Number of threads processing request */  
90 - int rqst_count; /* Total number of processed requests */  
91 -} srv_socket[MAX_BIND_ADDRS]; /* Array of listening sockets */  
92 -  
93 -static int sk_count = 0; /* Number of listening sockets */  
94 -  
95 -static int vp_count = 0; /* Number of server processes (VPs) */  
96 -static pid_t *vp_pids; /* Array of VP pids */  
97 -  
98 -static int my_index = -1; /* Current process index */  
99 -static pid_t my_pid = -1; /* Current process pid */  
100 -  
101 -static st_netfd_t sig_pipe[2]; /* Signal pipe */  
102 -  
103 -/*  
104 - * Configuration flags/parameters  
105 - */  
106 -static int interactive_mode = 0;  
107 -static int serialize_accept = 0;  
108 -static int log_access = 0;  
109 -static char *logdir = NULL;  
110 -static char *username = NULL;  
111 -static int listenq_size = LISTENQ_SIZE_DEFAULT;  
112 -static int errfd = STDERR_FILENO;  
113 -  
114 -/*  
115 - * Thread throttling parameters (all numbers are per listening socket).  
116 - * Zero values mean use default.  
117 - */  
118 -static int max_threads = 0; /* Max number of threads */  
119 -static int max_wait_threads = 0; /* Max number of "spare" threads */  
120 -static int min_wait_threads = 2; /* Min number of "spare" threads */  
121 -  
122 -  
123 -/******************************************************************  
124 - * Useful macros  
125 - */  
126 -  
127 -#ifndef INADDR_NONE  
128 -#define INADDR_NONE 0xffffffff  
129 -#endif  
130 -  
131 -#define SEC2USEC(s) ((s)*1000000LL)  
132 -  
133 -#define WAIT_THREADS(i) (srv_socket[i].wait_threads)  
134 -#define BUSY_THREADS(i) (srv_socket[i].busy_threads)  
135 -#define TOTAL_THREADS(i) (WAIT_THREADS(i) + BUSY_THREADS(i))  
136 -#define RQST_COUNT(i) (srv_socket[i].rqst_count)  
137 -  
138 -  
139 -/******************************************************************  
140 - * Forward declarations  
141 - */  
142 -  
143 -static void usage(const char *progname);  
144 -static void parse_arguments(int argc, char *argv[]);  
145 -static void start_daemon(void);  
146 -static void set_thread_throttling(void);  
147 -static void create_listeners(void);  
148 -static void change_user(void);  
149 -static void open_log_files(void);  
150 -static void start_processes(void);  
151 -static void wdog_sighandler(int signo);  
152 -static void child_sighandler(int signo);  
153 -static void install_sighandlers(void);  
154 -static void start_threads(void);  
155 -static void *process_signals(void *arg);  
156 -static void *flush_acclog_buffer(void *arg);  
157 -static void *handle_connections(void *arg);  
158 -static void dump_server_info(void);  
159 -  
160 -static void Signal(int sig, void (*handler)(int));  
161 -static int cpu_count(void);  
162 -  
163 -extern void handle_session(long srv_socket_index, st_netfd_t cli_nfd);  
164 -extern void load_configs(void);  
165 -extern void logbuf_open(void);  
166 -extern void logbuf_flush(void);  
167 -extern void logbuf_close(void);  
168 -  
169 -/* Error reporting functions defined in the error.c file */  
170 -extern void err_sys_report(int fd, const char *fmt, ...);  
171 -extern void err_sys_quit(int fd, const char *fmt, ...);  
172 -extern void err_sys_dump(int fd, const char *fmt, ...);  
173 -extern void err_report(int fd, const char *fmt, ...);  
174 -extern void err_quit(int fd, const char *fmt, ...);  
175 -  
176 -  
177 -/*  
178 - * General server example: accept a client connection and do something.  
179 - * This program just outputs a short HTML page, but can be easily adapted  
180 - * to do other things.  
181 - *  
182 - * This server creates a constant number of processes ("virtual processors"  
183 - * or VPs) and replaces them when they die. Each virtual processor manages  
184 - * its own independent set of state threads (STs), the number of which varies  
185 - * with load against the server. Each state thread listens to exactly one  
186 - * listening socket. The initial process becomes the watchdog, waiting for  
187 - * children (VPs) to die or for a signal requesting termination or restart.  
188 - * Upon receiving a restart signal (SIGHUP), all VPs close and then reopen  
189 - * log files and reload configuration. All currently active connections remain  
190 - * active. It is assumed that new configuration affects only request  
191 - * processing and not the general server parameters such as number of VPs,  
192 - * thread limits, bind addresses, etc. Those are specified as command line  
193 - * arguments, so the server has to be stopped and then started again in order  
194 - * to change them.  
195 - *  
196 - * Each state thread loops processing connections from a single listening  
197 - * socket. Only one ST runs on a VP at a time, and VPs do not share memory,  
198 - * so no mutual exclusion locking is necessary on any data, and the entire  
199 - * server is free to use all the static variables and non-reentrant library  
200 - * functions it wants, greatly simplifying programming and debugging and  
201 - * increasing performance (for example, it is safe to ++ and -- all global  
202 - * counters or call inet_ntoa(3) without any mutexes). The current thread on  
203 - * each VP maintains equilibrium on that VP, starting a new thread or  
204 - * terminating itself if the number of spare threads exceeds the lower or  
205 - * upper limit.  
206 - *  
207 - * All I/O operations on sockets must use the State Thread library's I/O  
208 - * functions because only those functions prevent blocking of the entire VP  
209 - * process and perform state thread scheduling.  
210 - */  
211 -int main(int argc, char *argv[])  
212 -{  
213 - /* Parse command-line options */  
214 - parse_arguments(argc, argv);  
215 -  
216 - /* Allocate array of server pids */  
217 - if ((vp_pids = calloc(vp_count, sizeof(pid_t))) == NULL)  
218 - err_sys_quit(errfd, "ERROR: calloc failed");  
219 -  
220 - /* Start the daemon */  
221 - if (!interactive_mode)  
222 - start_daemon();  
223 -  
224 - /* Initialize the ST library */  
225 - if (st_init() < 0)  
226 - err_sys_quit(errfd, "ERROR: initialization failed: st_init");  
227 -  
228 - /* Set thread throttling parameters */  
229 - set_thread_throttling();  
230 -  
231 - /* Create listening sockets */  
232 - create_listeners();  
233 -  
234 - /* Change the user */  
235 - if (username)  
236 - change_user();  
237 -  
238 - /* Open log files */  
239 - open_log_files();  
240 -  
241 - /* Start server processes (VPs) */  
242 - start_processes();  
243 -  
244 - /* Turn time caching on */  
245 - st_timecache_set(1);  
246 -  
247 - /* Install signal handlers */  
248 - install_sighandlers();  
249 -  
250 - /* Load configuration from config files */  
251 - load_configs();  
252 -  
253 - /* Start all threads */  
254 - start_threads();  
255 -  
256 - /* Become a signal processing thread */  
257 - process_signals(NULL);  
258 -  
259 - /* NOTREACHED */  
260 - return 1;  
261 -}  
262 -  
263 -  
264 -/******************************************************************/  
265 -  
266 -static void usage(const char *progname)  
267 -{  
268 - fprintf(stderr, "Usage: %s -l <log_directory> [<options>]\n\n"  
269 - "Possible options:\n\n"  
270 - "\t-b <host>:<port> Bind to specified address. Multiple"  
271 - " addresses\n"  
272 - "\t are permitted.\n"  
273 - "\t-p <num_processes> Create specified number of processes.\n"  
274 - "\t-t <min_thr>:<max_thr> Specify thread limits per listening"  
275 - " socket\n"  
276 - "\t across all processes.\n"  
277 - "\t-u <user> Change server's user id to specified"  
278 - " value.\n"  
279 - "\t-q <backlog> Set max length of pending connections"  
280 - " queue.\n"  
281 - "\t-a Enable access logging.\n"  
282 - "\t-i Run in interactive mode.\n"  
283 - "\t-S Serialize all accept() calls.\n"  
284 - "\t-h Print this message.\n",  
285 - progname);  
286 - exit(1);  
287 -}  
288 -  
289 -  
290 -/******************************************************************/  
291 -  
292 -static void parse_arguments(int argc, char *argv[])  
293 -{  
294 - extern char *optarg;  
295 - int opt;  
296 - char *c;  
297 -  
298 - while ((opt = getopt(argc, argv, "b:p:l:t:u:q:aiSh")) != EOF) {  
299 - switch (opt) {  
300 - case 'b':  
301 - if (sk_count >= MAX_BIND_ADDRS)  
302 - err_quit(errfd, "ERROR: max number of bind addresses (%d) exceeded",  
303 - MAX_BIND_ADDRS);  
304 - if ((c = strdup(optarg)) == NULL)  
305 - err_sys_quit(errfd, "ERROR: strdup");  
306 - srv_socket[sk_count++].addr = c;  
307 - break;  
308 - case 'p':  
309 - vp_count = atoi(optarg);  
310 - if (vp_count < 1)  
311 - err_quit(errfd, "ERROR: invalid number of processes: %s", optarg);  
312 - break;  
313 - case 'l':  
314 - logdir = optarg;  
315 - break;  
316 - case 't':  
317 - max_wait_threads = (int) strtol(optarg, &c, 10);  
318 - if (*c++ == ':')  
319 - max_threads = atoi(c);  
320 - if (max_wait_threads < 0 || max_threads < 0)  
321 - err_quit(errfd, "ERROR: invalid number of threads: %s", optarg);  
322 - break;  
323 - case 'u':  
324 - username = optarg;  
325 - break;  
326 - case 'q':  
327 - listenq_size = atoi(optarg);  
328 - if (listenq_size < 1)  
329 - err_quit(errfd, "ERROR: invalid listen queue size: %s", optarg);  
330 - break;  
331 - case 'a':  
332 - log_access = 1;  
333 - break;  
334 - case 'i':  
335 - interactive_mode = 1;  
336 - break;  
337 - case 'S':  
338 - /*  
339 - * Serialization decision is tricky on some platforms. For example,  
340 - * Solaris 2.6 and above has kernel sockets implementation, so supposedly  
341 - * there is no need for serialization. The ST library may be compiled  
342 - * on one OS version, but used on another, so the need for serialization  
343 - * should be determined at run time by the application. Since it's just  
344 - * an example, the serialization decision is left up to user.  
345 - * Only on platforms where the serialization is never needed on any OS  
346 - * version st_netfd_serialize_accept() is a no-op.  
347 - */  
348 - serialize_accept = 1;  
349 - break;  
350 - case 'h':  
351 - case '?':  
352 - usage(argv[0]);  
353 - }  
354 - }  
355 -  
356 - if (logdir == NULL && !interactive_mode) {  
357 - err_report(errfd, "ERROR: logging directory is required\n");  
358 - usage(argv[0]);  
359 - }  
360 -  
361 - if (getuid() == 0 && username == NULL)  
362 - err_report(errfd, "WARNING: running as super-user!");  
363 -  
364 - if (vp_count == 0 && (vp_count = cpu_count()) < 1)  
365 - vp_count = 1;  
366 -  
367 - if (sk_count == 0) {  
368 - sk_count = 1;  
369 - srv_socket[0].addr = "0.0.0.0";  
370 - }  
371 -}  
372 -  
373 -  
374 -/******************************************************************/  
375 -  
376 -static void start_daemon(void)  
377 -{  
378 - pid_t pid;  
379 -  
380 - /* Start forking */  
381 - if ((pid = fork()) < 0)  
382 - err_sys_quit(errfd, "ERROR: fork");  
383 - if (pid > 0)  
384 - exit(0); /* parent */  
385 -  
386 - /* First child process */  
387 - setsid(); /* become session leader */  
388 -  
389 - if ((pid = fork()) < 0)  
390 - err_sys_quit(errfd, "ERROR: fork");  
391 - if (pid > 0) /* first child */  
392 - exit(0);  
393 -  
394 - umask(022);  
395 -  
396 - if (chdir(logdir) < 0)  
397 - err_sys_quit(errfd, "ERROR: can't change directory to %s: chdir", logdir);  
398 -}  
399 -  
400 -  
401 -/******************************************************************  
402 - * For simplicity, the minimal size of thread pool is considered  
403 - * as a maximum number of spare threads (max_wait_threads) that  
404 - * will be created upon server startup. The pool size can grow up  
405 - * to the max_threads value. Note that this is a per listening  
406 - * socket limit. It is also possible to limit the total number of  
407 - * threads for all sockets rather than impose a per socket limit.  
408 - */  
409 -  
410 -static void set_thread_throttling(void)  
411 -{  
412 - /*  
413 - * Calculate total values across all processes.  
414 - * All numbers are per listening socket.  
415 - */  
416 - if (max_wait_threads == 0)  
417 - max_wait_threads = MAX_WAIT_THREADS_DEFAULT * vp_count;  
418 - /* Assuming that each client session needs FD_PER_THREAD file descriptors */  
419 - if (max_threads == 0)  
420 - max_threads = (st_getfdlimit() * vp_count) / FD_PER_THREAD / sk_count;  
421 - if (max_wait_threads > max_threads)  
422 - max_wait_threads = max_threads;  
423 -  
424 - /*  
425 - * Now calculate per-process values.  
426 - */  
427 - if (max_wait_threads % vp_count)  
428 - max_wait_threads = max_wait_threads / vp_count + 1;  
429 - else  
430 - max_wait_threads = max_wait_threads / vp_count;  
431 - if (max_threads % vp_count)  
432 - max_threads = max_threads / vp_count + 1;  
433 - else  
434 - max_threads = max_threads / vp_count;  
435 -  
436 - if (min_wait_threads > max_wait_threads)  
437 - min_wait_threads = max_wait_threads;  
438 -}  
439 -  
440 -  
441 -/******************************************************************/  
442 -  
443 -static void create_listeners(void)  
444 -{  
445 - int i, n, sock;  
446 - char *c;  
447 - struct sockaddr_in serv_addr;  
448 - struct hostent *hp;  
449 - unsigned short port;  
450 -  
451 - for (i = 0; i < sk_count; i++) {  
452 - port = 0;  
453 - if ((c = strchr(srv_socket[i].addr, ':')) != NULL) {  
454 - *c++ = '\0';  
455 - port = (unsigned short) atoi(c);  
456 - }  
457 - if (srv_socket[i].addr[0] == '\0')  
458 - srv_socket[i].addr = "0.0.0.0";  
459 - if (port == 0)  
460 - port = SERV_PORT_DEFAULT;  
461 -  
462 - /* Create server socket */  
463 - if ((sock = socket(PF_INET, SOCK_STREAM, 0)) < 0)  
464 - err_sys_quit(errfd, "ERROR: can't create socket: socket");  
465 - n = 1;  
466 - if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&n, sizeof(n)) < 0)  
467 - err_sys_quit(errfd, "ERROR: can't set SO_REUSEADDR: setsockopt");  
468 - memset(&serv_addr, 0, sizeof(serv_addr));  
469 - serv_addr.sin_family = AF_INET;  
470 - serv_addr.sin_port = htons(port);  
471 - serv_addr.sin_addr.s_addr = inet_addr(srv_socket[i].addr);  
472 - if (serv_addr.sin_addr.s_addr == INADDR_NONE) {  
473 - /* not dotted-decimal */  
474 - if ((hp = gethostbyname(srv_socket[i].addr)) == NULL)  
475 - err_quit(errfd, "ERROR: can't resolve address: %s",  
476 - srv_socket[i].addr);  
477 - memcpy(&serv_addr.sin_addr, hp->h_addr, hp->h_length);  
478 - }  
479 - srv_socket[i].port = port;  
480 -  
481 - /* Do bind and listen */  
482 - if (bind(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0)  
483 - err_sys_quit(errfd, "ERROR: can't bind to address %s, port %hu",  
484 - srv_socket[i].addr, port);  
485 - if (listen(sock, listenq_size) < 0)  
486 - err_sys_quit(errfd, "ERROR: listen");  
487 -  
488 - /* Create file descriptor object from OS socket */  
489 - if ((srv_socket[i].nfd = st_netfd_open_socket(sock)) == NULL)  
490 - err_sys_quit(errfd, "ERROR: st_netfd_open_socket");  
491 - /*  
492 - * On some platforms (e.g. IRIX, Linux) accept() serialization is never  
493 - * needed for any OS version. In that case st_netfd_serialize_accept()  
494 - * is just a no-op. Also see the comment above.  
495 - */  
496 - if (serialize_accept && st_netfd_serialize_accept(srv_socket[i].nfd) < 0)  
497 - err_sys_quit(errfd, "ERROR: st_netfd_serialize_accept");  
498 - }  
499 -}  
500 -  
501 -  
502 -/******************************************************************/  
503 -  
504 -static void change_user(void)  
505 -{  
506 - struct passwd *pw;  
507 -  
508 - if ((pw = getpwnam(username)) == NULL)  
509 - err_quit(errfd, "ERROR: can't find user '%s': getpwnam failed", username);  
510 -  
511 - if (setgid(pw->pw_gid) < 0)  
512 - err_sys_quit(errfd, "ERROR: can't change group id: setgid");  
513 - if (setuid(pw->pw_uid) < 0)  
514 - err_sys_quit(errfd, "ERROR: can't change user id: setuid");  
515 -  
516 - err_report(errfd, "INFO: changed process user id to '%s'", username);  
517 -}  
518 -  
519 -  
520 -/******************************************************************/  
521 -  
522 -static void open_log_files(void)  
523 -{  
524 - int fd;  
525 - char str[32];  
526 -  
527 - if (interactive_mode)  
528 - return;  
529 -  
530 - /* Open access log */  
531 - if (log_access)  
532 - logbuf_open();  
533 -  
534 - /* Open and write pid to pid file */  
535 - if ((fd = open(PID_FILE, O_CREAT | O_WRONLY | O_TRUNC, 0644)) < 0)  
536 - err_sys_quit(errfd, "ERROR: can't open pid file: open");  
537 - sprintf(str, "%d\n", (int)getpid());  
538 - if (write(fd, str, strlen(str)) != strlen(str))  
539 - err_sys_quit(errfd, "ERROR: can't write to pid file: write");  
540 - close(fd);  
541 -  
542 - /* Open error log file */  
543 - if ((fd = open(ERRORS_FILE, O_CREAT | O_WRONLY | O_APPEND, 0644)) < 0)  
544 - err_sys_quit(errfd, "ERROR: can't open error log file: open");  
545 - errfd = fd;  
546 -  
547 - err_report(errfd, "INFO: starting the server...");  
548 -}  
549 -  
550 -  
551 -/******************************************************************/  
552 -  
553 -static void start_processes(void)  
554 -{  
555 - int i, status;  
556 - pid_t pid;  
557 - sigset_t mask, omask;  
558 -  
559 - if (interactive_mode) {  
560 - my_index = 0;  
561 - my_pid = getpid();  
562 - return;  
563 - }  
564 -  
565 - for (i = 0; i < vp_count; i++) {  
566 - if ((pid = fork()) < 0) {  
567 - err_sys_report(errfd, "ERROR: can't create process: fork");  
568 - if (i == 0)  
569 - exit(1);  
570 - err_report(errfd, "WARN: started only %d processes out of %d", i,  
571 - vp_count);  
572 - vp_count = i;  
573 - break;  
574 - }  
575 - if (pid == 0) {  
576 - my_index = i;  
577 - my_pid = getpid();  
578 - /* Child returns to continue in main() */  
579 - return;  
580 - }  
581 - vp_pids[i] = pid;  
582 - }  
583 -  
584 - /*  
585 - * Parent process becomes a "watchdog" and never returns to main().  
586 - */  
587 -  
588 - /* Install signal handlers */  
589 - Signal(SIGTERM, wdog_sighandler); /* terminate */  
590 - Signal(SIGHUP, wdog_sighandler); /* restart */  
591 - Signal(SIGUSR1, wdog_sighandler); /* dump info */  
592 -  
593 - /* Now go to sleep waiting for a child termination or a signal */  
594 - for ( ; ; ) {  
595 - if ((pid = wait(&status)) < 0) {  
596 - if (errno == EINTR)  
597 - continue;  
598 - err_sys_quit(errfd, "ERROR: watchdog: wait");  
599 - }  
600 - /* Find index of the exited child */  
601 - for (i = 0; i < vp_count; i++) {  
602 - if (vp_pids[i] == pid)  
603 - break;  
604 - }  
605 -  
606 - /* Block signals while printing and forking */  
607 - sigemptyset(&mask);  
608 - sigaddset(&mask, SIGTERM);  
609 - sigaddset(&mask, SIGHUP);  
610 - sigaddset(&mask, SIGUSR1);  
611 - sigprocmask(SIG_BLOCK, &mask, &omask);  
612 -  
613 - if (WIFEXITED(status))  
614 - err_report(errfd, "WARN: watchdog: process %d (pid %d) exited"  
615 - " with status %d", i, pid, WEXITSTATUS(status));  
616 - else if (WIFSIGNALED(status))  
617 - err_report(errfd, "WARN: watchdog: process %d (pid %d) terminated"  
618 - " by signal %d", i, pid, WTERMSIG(status));  
619 - else if (WIFSTOPPED(status))  
620 - err_report(errfd, "WARN: watchdog: process %d (pid %d) stopped"  
621 - " by signal %d", i, pid, WSTOPSIG(status));  
622 - else  
623 - err_report(errfd, "WARN: watchdog: process %d (pid %d) terminated:"  
624 - " unknown termination reason", i, pid);  
625 -  
626 - /* Fork another VP */  
627 - if ((pid = fork()) < 0) {  
628 - err_sys_report(errfd, "ERROR: watchdog: can't create process: fork");  
629 - } else if (pid == 0) {  
630 - my_index = i;  
631 - my_pid = getpid();  
632 - /* Child returns to continue in main() */  
633 - return;  
634 - }  
635 - vp_pids[i] = pid;  
636 -  
637 - /* Restore the signal mask */  
638 - sigprocmask(SIG_SETMASK, &omask, NULL);  
639 - }  
640 -}  
641 -  
642 -  
643 -/******************************************************************/  
644 -  
645 -static void wdog_sighandler(int signo)  
646 -{  
647 - int i, err;  
648 -  
649 - /* Save errno */  
650 - err = errno;  
651 - /* Forward the signal to all children */  
652 - for (i = 0; i < vp_count; i++) {  
653 - if (vp_pids[i] > 0)  
654 - kill(vp_pids[i], signo);  
655 - }  
656 - /*  
657 - * It is safe to do pretty much everything here because process is  
658 - * sleeping in wait() which is async-safe.  
659 - */  
660 - switch (signo) {  
661 - case SIGHUP:  
662 - err_report(errfd, "INFO: watchdog: caught SIGHUP");  
663 - /* Reopen log files - needed for log rotation */  
664 - if (log_access) {  
665 - logbuf_close();  
666 - logbuf_open();  
667 - }  
668 - close(errfd);  
669 - if ((errfd = open(ERRORS_FILE, O_CREAT | O_WRONLY | O_APPEND, 0644)) < 0)  
670 - err_sys_quit(STDERR_FILENO, "ERROR: watchdog: open");  
671 - break;  
672 - case SIGTERM:  
673 - /* Non-graceful termination */  
674 - err_report(errfd, "INFO: watchdog: caught SIGTERM, terminating");  
675 - unlink(PID_FILE);  
676 - exit(0);  
677 - case SIGUSR1:  
678 - err_report(errfd, "INFO: watchdog: caught SIGUSR1");  
679 - break;  
680 - default:  
681 - err_report(errfd, "INFO: watchdog: caught signal %d", signo);  
682 - }  
683 - /* Restore errno */  
684 - errno = err;  
685 -}  
686 -  
687 -  
688 -/******************************************************************/  
689 -  
690 -static void install_sighandlers(void)  
691 -{  
692 - sigset_t mask;  
693 - int p[2];  
694 -  
695 - /* Create signal pipe */  
696 - if (pipe(p) < 0)  
697 - err_sys_quit(errfd, "ERROR: process %d (pid %d): can't create"  
698 - " signal pipe: pipe", my_index, my_pid);  
699 - if ((sig_pipe[0] = st_netfd_open(p[0])) == NULL ||  
700 - (sig_pipe[1] = st_netfd_open(p[1])) == NULL)  
701 - err_sys_quit(errfd, "ERROR: process %d (pid %d): can't create"  
702 - " signal pipe: st_netfd_open", my_index, my_pid);  
703 -  
704 - /* Install signal handlers */  
705 - Signal(SIGTERM, child_sighandler); /* terminate */  
706 - Signal(SIGHUP, child_sighandler); /* restart */  
707 - Signal(SIGUSR1, child_sighandler); /* dump info */  
708 -  
709 - /* Unblock signals */  
710 - sigemptyset(&mask);  
711 - sigaddset(&mask, SIGTERM);  
712 - sigaddset(&mask, SIGHUP);  
713 - sigaddset(&mask, SIGUSR1);  
714 - sigprocmask(SIG_UNBLOCK, &mask, NULL);  
715 -}  
716 -  
717 -  
718 -/******************************************************************/  
719 -  
720 -static void child_sighandler(int signo)  
721 -{  
722 - int err, fd;  
723 -  
724 - err = errno;  
725 - fd = st_netfd_fileno(sig_pipe[1]);  
726 -  
727 - /* write() is async-safe */  
728 - if (write(fd, &signo, sizeof(int)) != sizeof(int))  
729 - err_sys_quit(errfd, "ERROR: process %d (pid %d): child's signal"  
730 - " handler: write", my_index, my_pid);  
731 - errno = err;  
732 -}  
733 -  
734 -  
735 -/******************************************************************  
736 - * The "main" function of the signal processing thread.  
737 - */  
738 -  
739 -/* ARGSUSED */  
740 -static void *process_signals(void *arg)  
741 -{  
742 - int signo;  
743 -  
744 - for ( ; ; ) {  
745 - /* Read the next signal from the signal pipe */  
746 - if (st_read(sig_pipe[0], &signo, sizeof(int),  
747 - ST_UTIME_NO_TIMEOUT) != sizeof(int))  
748 - err_sys_quit(errfd, "ERROR: process %d (pid %d): signal processor:"  
749 - " st_read", my_index, my_pid);  
750 -  
751 - switch (signo) {  
752 - case SIGHUP:  
753 - err_report(errfd, "INFO: process %d (pid %d): caught SIGHUP,"  
754 - " reloading configuration", my_index, my_pid);  
755 - if (interactive_mode) {  
756 - load_configs();  
757 - break;  
758 - }  
759 - /* Reopen log files - needed for log rotation */  
760 - if (log_access) {  
761 - logbuf_flush();  
762 - logbuf_close();  
763 - logbuf_open();  
764 - }  
765 - close(errfd);  
766 - if ((errfd = open(ERRORS_FILE, O_CREAT | O_WRONLY | O_APPEND, 0644)) < 0)  
767 - err_sys_quit(STDERR_FILENO, "ERROR: process %d (pid %d): signal"  
768 - " processor: open", my_index, my_pid);  
769 - /* Reload configuration */  
770 - load_configs();  
771 - break;  
772 - case SIGTERM:  
773 - /*  
774 - * Terminate ungracefully since it is generally not known how long  
775 - * it will take to gracefully complete all client sessions.  
776 - */  
777 - err_report(errfd, "INFO: process %d (pid %d): caught SIGTERM,"  
778 - " terminating", my_index, my_pid);  
779 - if (log_access)  
780 - logbuf_flush();  
781 - exit(0);  
782 - case SIGUSR1:  
783 - err_report(errfd, "INFO: process %d (pid %d): caught SIGUSR1",  
784 - my_index, my_pid);  
785 - /* Print server info to stderr */  
786 - dump_server_info();  
787 - break;  
788 - default:  
789 - err_report(errfd, "INFO: process %d (pid %d): caught signal %d",  
790 - my_index, my_pid, signo);  
791 - }  
792 - }  
793 -  
794 - /* NOTREACHED */  
795 - return NULL;  
796 -}  
797 -  
798 -  
799 -/******************************************************************  
800 - * The "main" function of the access log flushing thread.  
801 - */  
802 -  
803 -/* ARGSUSED */  
804 -static void *flush_acclog_buffer(void *arg)  
805 -{  
806 - for ( ; ; ) {  
807 - st_sleep(ACCLOG_FLUSH_INTERVAL);  
808 - logbuf_flush();  
809 - }  
810 -  
811 - /* NOTREACHED */  
812 - return NULL;  
813 -}  
814 -  
815 -  
816 -/******************************************************************/  
817 -  
818 -static void start_threads(void)  
819 -{  
820 - long i, n;  
821 -  
822 - /* Create access log flushing thread */  
823 - if (log_access && st_thread_create(flush_acclog_buffer, NULL, 0, 0) == NULL)  
824 - err_sys_quit(errfd, "ERROR: process %d (pid %d): can't create"  
825 - " log flushing thread", my_index, my_pid);  
826 -  
827 - /* Create connections handling threads */  
828 - for (i = 0; i < sk_count; i++) {  
829 - err_report(errfd, "INFO: process %d (pid %d): starting %d threads"  
830 - " on %s:%u", my_index, my_pid, max_wait_threads,  
831 - srv_socket[i].addr, srv_socket[i].port);  
832 - WAIT_THREADS(i) = 0;  
833 - BUSY_THREADS(i) = 0;  
834 - RQST_COUNT(i) = 0;  
835 - for (n = 0; n < max_wait_threads; n++) {  
836 - if (st_thread_create(handle_connections, (void *)i, 0, 0) != NULL)  
837 - WAIT_THREADS(i)++;  
838 - else  
839 - err_sys_report(errfd, "ERROR: process %d (pid %d): can't create"  
840 - " thread", my_index, my_pid);  
841 - }  
842 - if (WAIT_THREADS(i) == 0)  
843 - exit(1);  
844 - }  
845 -}  
846 -  
847 -  
848 -/******************************************************************/  
849 -  
850 -static void *handle_connections(void *arg)  
851 -{  
852 - st_netfd_t srv_nfd, cli_nfd;  
853 - struct sockaddr_in from;  
854 - int fromlen;  
855 - long i = (long) arg;  
856 -  
857 - srv_nfd = srv_socket[i].nfd;  
858 - fromlen = sizeof(from);  
859 -  
860 - while (WAIT_THREADS(i) <= max_wait_threads) {  
861 - cli_nfd = st_accept(srv_nfd, (struct sockaddr *)&from, &fromlen,  
862 - ST_UTIME_NO_TIMEOUT);  
863 - if (cli_nfd == NULL) {  
864 - err_sys_report(errfd, "ERROR: can't accept connection: st_accept");  
865 - continue;  
866 - }  
867 - /* Save peer address, so we can retrieve it later */  
868 - st_netfd_setspecific(cli_nfd, &from.sin_addr, NULL);  
869 -  
870 - WAIT_THREADS(i)--;  
871 - BUSY_THREADS(i)++;  
872 - if (WAIT_THREADS(i) < min_wait_threads && TOTAL_THREADS(i) < max_threads) {  
873 - /* Create another spare thread */  
874 - if (st_thread_create(handle_connections, (void *)i, 0, 0) != NULL)  
875 - WAIT_THREADS(i)++;  
876 - else  
877 - err_sys_report(errfd, "ERROR: process %d (pid %d): can't create"  
878 - " thread", my_index, my_pid);  
879 - }  
880 -  
881 - handle_session(i, cli_nfd);  
882 -  
883 - st_netfd_close(cli_nfd);  
884 - WAIT_THREADS(i)++;  
885 - BUSY_THREADS(i)--;  
886 - }  
887 -  
888 - WAIT_THREADS(i)--;  
889 - return NULL;  
890 -}  
891 -  
892 -  
893 -/******************************************************************/  
894 -  
895 -static void dump_server_info(void)  
896 -{  
897 - char *buf;  
898 - int i, len;  
899 -  
900 - if ((buf = malloc(sk_count * 512)) == NULL) {  
901 - err_sys_report(errfd, "ERROR: malloc failed");  
902 - return;  
903 - }  
904 -  
905 - len = sprintf(buf, "\n\nProcess #%d (pid %d):\n", my_index, (int)my_pid);  
906 - for (i = 0; i < sk_count; i++) {  
907 - len += sprintf(buf + len, "\nListening Socket #%d:\n"  
908 - "-------------------------\n"  
909 - "Address %s:%u\n"  
910 - "Thread limits (min/max) %d/%d\n"  
911 - "Waiting threads %d\n"  
912 - "Busy threads %d\n"  
913 - "Requests served %d\n",  
914 - i, srv_socket[i].addr, srv_socket[i].port,  
915 - max_wait_threads, max_threads,  
916 - WAIT_THREADS(i), BUSY_THREADS(i), RQST_COUNT(i));  
917 - }  
918 -  
919 - write(STDERR_FILENO, buf, len);  
920 - free(buf);  
921 -}  
922 -  
923 -  
924 -/******************************************************************  
925 - * Stubs  
926 - */  
927 -  
928 -/*  
929 - * Session handling function stub. Just dumps small HTML page.  
930 - */  
931 -void handle_session(long srv_socket_index, st_netfd_t cli_nfd)  
932 -{  
933 - static char resp[] = "HTTP/1.0 200 OK\r\nContent-type: text/html\r\n"  
934 - "Connection: close\r\n\r\n<H2>It worked!</H2>\n";  
935 - char buf[512];  
936 - int n = sizeof(resp) - 1;  
937 - struct in_addr *from = st_netfd_getspecific(cli_nfd);  
938 -  
939 - if (st_read(cli_nfd, buf, sizeof(buf), SEC2USEC(REQUEST_TIMEOUT)) < 0) {  
940 - err_sys_report(errfd, "WARN: can't read request from %s: st_read",  
941 - inet_ntoa(*from));  
942 - return;  
943 - }  
944 - if (st_write(cli_nfd, resp, n, ST_UTIME_NO_TIMEOUT) != n) {  
945 - err_sys_report(errfd, "WARN: can't write response to %s: st_write",  
946 - inet_ntoa(*from));  
947 - return;  
948 - }  
949 -  
950 - RQST_COUNT(srv_socket_index)++;  
951 -}  
952 -  
953 -  
954 -/*  
955 - * Configuration loading function stub.  
956 - */  
957 -void load_configs(void)  
958 -{  
959 - err_report(errfd, "INFO: process %d (pid %d): configuration loaded",  
960 - my_index, my_pid);  
961 -}  
962 -  
963 -  
964 -/*  
965 - * Buffered access logging methods.  
966 - * Note that stdio functions (fopen(3), fprintf(3), fflush(3), etc.) cannot  
967 - * be used if multiple VPs are created since these functions can flush buffer  
968 - * at any point and thus write only partial log record to disk.  
969 - * Also, it is completely safe for all threads of the same VP to write to  
970 - * the same log buffer without any mutex protection (one buffer per VP, of  
971 - * course).  
972 - */  
973 -void logbuf_open(void)  
974 -{  
975 -  
976 -}  
977 -  
978 -  
979 -void logbuf_flush(void)  
980 -{  
981 -  
982 -}  
983 -  
984 -  
985 -void logbuf_close(void)  
986 -{  
987 -  
988 -}  
989 -  
990 -  
991 -/******************************************************************  
992 - * Small utility functions  
993 - */  
994 -  
995 -static void Signal(int sig, void (*handler)(int))  
996 -{  
997 - struct sigaction sa;  
998 -  
999 - sa.sa_handler = handler;  
1000 - sigemptyset(&sa.sa_mask);  
1001 - sa.sa_flags = 0;  
1002 - sigaction(sig, &sa, NULL);  
1003 -}  
1004 -  
1005 -static int cpu_count(void)  
1006 -{  
1007 - int n;  
1008 -  
1009 -#if defined (_SC_NPROCESSORS_ONLN)  
1010 - n = (int) sysconf(_SC_NPROCESSORS_ONLN);  
1011 -#elif defined (_SC_NPROC_ONLN)  
1012 - n = (int) sysconf(_SC_NPROC_ONLN);  
1013 -#elif defined (HPUX)  
1014 -#include <sys/mpctl.h>  
1015 - n = mpctl(MPC_GETNUMSPUS, 0, 0);  
1016 -#else  
1017 - n = -1;  
1018 - errno = ENOSYS;  
1019 -#endif  
1020 -  
1021 - return n;  
1022 -}  
1023 -  
1024 -/******************************************************************/  
1025 -  
1 -#  
2 -# Portions created by SGI are Copyright (C) 2000 Silicon Graphics, Inc.  
3 -# All Rights Reserved.  
4 -#  
5 -# Redistribution and use in source and binary forms, with or without  
6 -# modification, are permitted provided that the following conditions  
7 -# are met:  
8 -#  
9 -# 1. Redistributions of source code must retain the above copyright  
10 -# notice, this list of conditions and the following disclaimer.  
11 -# 2. Redistributions in binary form must reproduce the above copyright  
12 -# notice, this list of conditions and the following disclaimer in the  
13 -# documentation and/or other materials provided with the distribution.  
14 -# 3. Neither the name of Silicon Graphics, Inc. nor the names of its  
15 -# contributors may be used to endorse or promote products derived from  
16 -# this software without specific prior written permission.  
17 -#  
18 -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS  
19 -# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT  
20 -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR  
21 -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT  
22 -# HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,  
23 -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED  
24 -# TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR  
25 -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF  
26 -# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING  
27 -# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS  
28 -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  
29 -  
30 -CC = cc  
31 -  
32 -SHELL = /bin/sh  
33 -ECHO = /bin/echo  
34 -  
35 -DEPTH = ..  
36 -BUILD =  
37 -TARGETDIR = obj  
38 -  
39 -DEFINES =  
40 -OTHER_FLAGS =  
41 -CFLAGS =  
42 -  
43 -OBJDIR = $(DEPTH)/$(TARGETDIR)  
44 -INCDIR = $(DEPTH)/$(TARGETDIR)  
45 -  
46 -LIBRESOLV =  
47 -EXTRALIBS =  
48 -  
49 -SLIBRARY = $(OBJDIR)/libstx.a  
50 -OBJS = $(OBJDIR)/dnscache.o $(OBJDIR)/dnsres.o $(OBJDIR)/lrucache.o  
51 -  
52 -  
53 -CFLAGS += -Wall -I$(INCDIR)  
54 -AR = ar  
55 -ARFLAGS = rv  
56 -RANLIB = ranlib  
57 -  
58 -  
59 -##########################  
60 -# Platform section.  
61 -#  
62 -  
63 -ifeq (LINUX, $(findstring LINUX, $(OS)))  
64 -LIBRESOLV = -lresolv  
65 -endif  
66 -  
67 -ifeq ($(OS), SOLARIS)  
68 -LIBRESOLV = -lresolv  
69 -EXTRALIBS = -lsocket -lnsl  
70 -endif  
71 -  
72 -#  
73 -# End of platform section.  
74 -##########################  
75 -  
76 -  
77 -all: $(SLIBRARY)  
78 -  
79 -$(SLIBRARY): $(OBJS)  
80 - $(AR) $(ARFLAGS) $@ $(OBJS)  
81 - $(RANLIB) $@  
82 -  
83 -$(OBJDIR)/%.o: %.c stx.h common.h  
84 - $(CC) $(CFLAGS) -c $< -o $@  
85 -  
86 -clean:  
87 - rm -rf $(OBJS) $(SLIBRARY)  
88 -  
89 -#.DEFAULT:  
90 -# @cd $(DEPTH); $(MAKE) $@  
91 -  
1 -This directory contains extensions to the core State Threads Library  
2 -that were contributed by users. All files hereunder are not part of the  
3 -State Threads Library itself. They are provided as-is, without warranty  
4 -or support, and under whatever license terms their authors provided. To  
5 -contribute your own extensions, just mail them to the project  
6 -administrators or to one of the project's mailing lists; see  
7 -state-threads.sourceforge.net. Please indicate the license terms under  
8 -which the project may distribute your contribution.  
9 -  
10 -========================================================================  
11 -  
12 -stx_fileio  
13 -----------  
14 -Contributed by Jeff <jlb-st@houseofdistraction.com>, 4 Nov 2002.  
15 -  
16 -Provides non-blocking random access file reading capability for  
17 -programs using the State Threads library. There is one public function:  
18 -  
19 -ssize_t stx_file_read(st_netfd_t fd, off_t offset,  
20 - void *buf, size_t nbytes, st_utime_t timeout);  
21 -  
22 -The implementation is not optimal in that the data is copied at least once  
23 -more than should be necessary. Its usefulness is limited to cases where  
24 -random access to a file is required and where starvation of other threads  
25 -is unacceptable.  
26 -  
27 -The particular application which motivated this implementation was a UDP  
28 -file transfer protocol. Because the OS does very little buffering of UDP  
29 -traffic it is important that UDP transmission threads are not starved for  
30 -periods of time which are long relative to the interval required to  
31 -maintain a steady send rate.  
32 -  
33 -Licensed under the same dual MPL/GPL as core State Threads.  
34 -  
35 -========================================================================  
36 -  
37 -stx_dns  
38 --------  
39 -  
40 -Documentation coming.  
41 -  
42 -========================================================================  
1 -#ifndef _STX_COMMON_H_  
2 -#define _STX_COMMON_H_  
3 -  
4 -#include <stddef.h>  
5 -#include <stdlib.h>  
6 -  
7 -  
8 -#define STX_BEGIN_MACRO {  
9 -#define STX_END_MACRO }  
10 -  
11 -  
12 -/*****************************************  
13 - * Circular linked list definitions  
14 - */  
15 -  
16 -typedef struct _stx_clist {  
17 - struct _stx_clist *next;  
18 - struct _stx_clist *prev;  
19 -} stx_clist_t;  
20 -  
21 -/* Insert element "_e" into the list, before "_l" */  
22 -#define STX_CLIST_INSERT_BEFORE(_e,_l) \  
23 - STX_BEGIN_MACRO \  
24 - (_e)->next = (_l); \  
25 - (_e)->prev = (_l)->prev; \  
26 - (_l)->prev->next = (_e); \  
27 - (_l)->prev = (_e); \  
28 - STX_END_MACRO  
29 -  
30 -/* Insert element "_e" into the list, after "_l" */  
31 -#define STX_CLIST_INSERT_AFTER(_e,_l) \  
32 - STX_BEGIN_MACRO \  
33 - (_e)->next = (_l)->next; \  
34 - (_e)->prev = (_l); \  
35 - (_l)->next->prev = (_e); \  
36 - (_l)->next = (_e); \  
37 - STX_END_MACRO  
38 -  
39 -/* Append an element "_e" to the end of the list "_l" */  
40 -#define STX_CLIST_APPEND_LINK(_e,_l) STX_CLIST_INSERT_BEFORE(_e,_l)  
41 -  
42 -/* Remove the element "_e" from it's circular list */  
43 -#define STX_CLIST_REMOVE_LINK(_e) \  
44 - STX_BEGIN_MACRO \  
45 - (_e)->prev->next = (_e)->next; \  
46 - (_e)->next->prev = (_e)->prev; \  
47 - STX_END_MACRO  
48 -  
49 -/* Return the head/tail of the list */  
50 -#define STX_CLIST_HEAD(_l) (_l)->next  
51 -#define STX_CLIST_TAIL(_l) (_l)->prev  
52 -  
53 -/* Return non-zero if the given circular list "_l" is empty, */  
54 -/* zero if the circular list is not empty */  
55 -#define STX_CLIST_IS_EMPTY(_l) \  
56 - ((_l)->next == (_l))  
57 -  
58 -/* Initialize a circular list */  
59 -#define STX_CLIST_INIT_CLIST(_l) \  
60 - STX_BEGIN_MACRO \  
61 - (_l)->next = (_l); \  
62 - (_l)->prev = (_l); \  
63 - STX_END_MACRO  
64 -  
65 -  
66 -/*****************************************  
67 - * Useful macros  
68 - */  
69 -  
70 -#ifndef offsetof  
71 -#define offsetof(type, identifier) ((size_t)&(((type *)0)->identifier))  
72 -#endif  
73 -  
74 -#define STX_MIN(a, b) (((a) < (b)) ? (a) : (b))  
75 -  
76 -#endif /* !_STX_COMMON_H_ */  
77 -  
1 -#include "stx.h"  
2 -#include "common.h"  
3 -  
4 -  
5 -/*****************************************  
6 - * Basic types definitions  
7 - */  
8 -  
9 -typedef struct _stx_dns_data {  
10 - struct in_addr *addrs;  
11 - int num_addrs;  
12 - int cur;  
13 - time_t expires;  
14 -} stx_dns_data_t;  
15 -  
16 -  
17 -#define MAX_HOST_ADDRS 1024  
18 -  
19 -static struct in_addr addr_list[MAX_HOST_ADDRS];  
20 -  
21 -stx_cache_t *_stx_dns_cache = NULL;  
22 -  
23 -extern int _stx_dns_ttl;  
24 -extern int _stx_dns_getaddrlist(const char *hostname, struct in_addr *addrs,  
25 - int *num_addrs, st_utime_t timeout);  
26 -  
27 -  
28 -static unsigned long hash_hostname(const void *key)  
29 -{  
30 - const char *name = (const char *)key;  
31 - unsigned long hash = 0;  
32 -  
33 - while (*name)  
34 - hash = (hash << 4) - hash + *name++; /* hash = hash * 15 + *name++ */  
35 -  
36 - return hash;  
37 -}  
38 -  
39 -static void cleanup_entry(void *key, void *data)  
40 -{  
41 - if (key)  
42 - free(key);  
43 -  
44 - if (data) {  
45 - if (((stx_dns_data_t *)data)->addrs)  
46 - free(((stx_dns_data_t *)data)->addrs);  
47 - free(data);  
48 - }  
49 -}  
50 -  
51 -static int lookup_entry(const char *host, struct in_addr *addrs,  
52 - int *num_addrs, int rotate)  
53 -{  
54 - stx_cache_entry_t *entry;  
55 - stx_dns_data_t *data;  
56 - int n;  
57 -  
58 - entry = stx_cache_entry_lookup(_stx_dns_cache, host);  
59 - if (entry) {  
60 - data = (stx_dns_data_t *)stx_cache_entry_getdata(entry);  
61 - if (st_time() <= data->expires) {  
62 - if (*num_addrs == 1) {  
63 - if (rotate) {  
64 - *addrs = data->addrs[data->cur++];  
65 - if (data->cur >= data->num_addrs)  
66 - data->cur = 0;  
67 - } else {  
68 - *addrs = data->addrs[0];  
69 - }  
70 - } else {  
71 - n = STX_MIN(*num_addrs, data->num_addrs);  
72 - memcpy(addrs, data->addrs, n * sizeof(*addrs));  
73 - *num_addrs = n;  
74 - }  
75 -  
76 - stx_cache_entry_release(_stx_dns_cache, entry);  
77 - return 1;  
78 - }  
79 -  
80 - /*  
81 - * Cache entry expired: decrement its refcount and purge it from cache.  
82 - */  
83 - stx_cache_entry_release(_stx_dns_cache, entry);  
84 - stx_cache_entry_delete(_stx_dns_cache, entry);  
85 - }  
86 -  
87 - return 0;  
88 -}  
89 -  
90 -static void insert_entry(const char *host, struct in_addr *addrs, int count)  
91 -{  
92 - stx_cache_entry_t *entry;  
93 - stx_dns_data_t *data;  
94 - char *key;  
95 - size_t n;  
96 -  
97 - if (_stx_dns_ttl > 0) {  
98 - key = strdup(host);  
99 - data = (stx_dns_data_t *)malloc(sizeof(stx_dns_data_t));  
100 - n = count * sizeof(*addrs);  
101 - if (data) {  
102 - data->addrs = (struct in_addr *)malloc(n);  
103 - if (data->addrs)  
104 - memcpy(data->addrs, addrs, n);  
105 - data->num_addrs = count;  
106 - data->cur = 0;  
107 - data->expires = st_time() + _stx_dns_ttl;  
108 - }  
109 - entry = stx_cache_entry_create(key, data, strlen(host) + 1 +  
110 - sizeof(stx_dns_data_t) + n +  
111 - stx_cache_entry_sizeof());  
112 - if (key && data && data->addrs && entry &&  
113 - stx_cache_entry_insert(_stx_dns_cache, entry) == 0) {  
114 - stx_cache_entry_release(_stx_dns_cache, entry);  
115 - return;  
116 - }  
117 -  
118 - if (entry)  
119 - stx_cache_entry_delete(_stx_dns_cache, entry);  
120 - else  
121 - cleanup_entry(key, data);  
122 - }  
123 -}  
124 -  
125 -  
126 -  
127 -int _stx_dns_cache_getaddrlist(const char *hostname, struct in_addr *addrs,  
128 - int *num_addrs, st_utime_t timeout,  
129 - int rotate)  
130 -{  
131 - char host[128];  
132 - int n, count;  
133 -  
134 - if (!_stx_dns_cache)  
135 - return _stx_dns_getaddrlist(hostname, addrs, num_addrs, timeout);  
136 -  
137 - for (n = 0; n < sizeof(host) - 1 && hostname[n]; n++) {  
138 - host[n] = tolower(hostname[n]);  
139 - }  
140 - host[n] = '\0';  
141 -  
142 - if (lookup_entry(host, addrs, num_addrs, rotate))  
143 - return 0;  
144 -  
145 - count = MAX_HOST_ADDRS;  
146 - if (_stx_dns_getaddrlist(host, addr_list, &count, timeout) < 0)  
147 - return -1;  
148 - n = STX_MIN(*num_addrs, count);  
149 - memcpy(addrs, addr_list, n * sizeof(*addrs));  
150 - *num_addrs = n;  
151 -  
152 - insert_entry(host, addr_list, count);  
153 - return 0;  
154 -}  
155 -  
156 -  
157 -int stx_dns_cache_init(size_t max_size, size_t max_bytes, size_t hash_size)  
158 -{  
159 - _stx_dns_cache = stx_cache_create(max_size, max_bytes, hash_size,  
160 - hash_hostname,  
161 - (long (*)(const void *, const void *))strcmp,  
162 - cleanup_entry);  
163 - if (!_stx_dns_cache)  
164 - return -1;  
165 -  
166 - return 0;  
167 -}  
168 -  
169 -void stx_dns_cache_getinfo(stx_cache_info_t *info)  
170 -{  
171 - if (_stx_dns_cache)  
172 - stx_cache_getinfo(_stx_dns_cache, info);  
173 - else  
174 - memset(info, 0, sizeof(stx_cache_info_t));  
175 -}  
176 -  
177 -int stx_dns_getaddrlist(const char *hostname, struct in_addr *addrs,  
178 - int *num_addrs, st_utime_t timeout)  
179 -{  
180 - return _stx_dns_cache_getaddrlist(hostname, addrs, num_addrs, timeout, 0);  
181 -}  
182 -  
183 -int stx_dns_getaddr(const char *hostname, struct in_addr *addr,  
184 - st_utime_t timeout)  
185 -{  
186 - int n = 1;  
187 -  
188 - return _stx_dns_cache_getaddrlist(hostname, addr, &n, timeout, 1);  
189 -}  
190 -  
1 -/*  
2 - * Copyright (c) 1985, 1988, 1993  
3 - * The Regents of the University of California. All rights reserved.  
4 - *  
5 - * Redistribution and use in source and binary forms, with or without  
6 - * modification, are permitted provided that the following conditions  
7 - * are met:  
8 - * 1. Redistributions of source code must retain the above copyright  
9 - * notice, this list of conditions and the following disclaimer.  
10 - * 2. Redistributions in binary form must reproduce the above copyright  
11 - * notice, this list of conditions and the following disclaimer in the  
12 - * documentation and/or other materials provided with the distribution.  
13 - * 3. All advertising materials mentioning features or use of this software  
14 - * must display the following acknowledgement:  
15 - * This product includes software developed by the University of  
16 - * California, Berkeley and its contributors.  
17 - * 4. Neither the name of the University nor the names of its contributors  
18 - * may be used to endorse or promote products derived from this software  
19 - * without specific prior written permission.  
20 - *  
21 - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND  
22 - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE  
23 - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE  
24 - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE  
25 - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL  
26 - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS  
27 - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)  
28 - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT  
29 - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY  
30 - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF  
31 - * SUCH DAMAGE.  
32 - *  
33 - * Portions created by SGI are Copyright (C) 2000 Silicon Graphics, Inc.  
34 - * All Rights Reserved.  
35 - *  
36 - * Redistribution and use in source and binary forms, with or without  
37 - * modification, are permitted provided that the following conditions  
38 - * are met:  
39 - *  
40 - * 1. Redistributions of source code must retain the above copyright  
41 - * notice, this list of conditions and the following disclaimer.  
42 - * 2. Redistributions in binary form must reproduce the above copyright  
43 - * notice, this list of conditions and the following disclaimer in the  
44 - * documentation and/or other materials provided with the distribution.  
45 - * 3. Neither the name of Silicon Graphics, Inc. nor the names of its  
46 - * contributors may be used to endorse or promote products derived from  
47 - * this software without specific prior written permission.  
48 - *  
49 - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS  
50 - * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT  
51 - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR  
52 - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT  
53 - * HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,  
54 - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED  
55 - * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR  
56 - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF  
57 - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING  
58 - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS  
59 - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  
60 - */  
61 -  
62 -#include "stx.h"  
63 -  
64 -#define MAXPACKET 1024  
65 -  
66 -#if !defined(NETDB_INTERNAL) && defined(h_NETDB_INTERNAL)  
67 -#define NETDB_INTERNAL h_NETDB_INTERNAL  
68 -#endif  
69 -  
70 -/* New in Solaris 7 */  
71 -#if !defined(_getshort) && defined(ns_get16)  
72 -#define _getshort(cp) ns_get16(cp)  
73 -#define _getlong(cp) ns_get32(cp)  
74 -#endif  
75 -  
76 -typedef union {  
77 - HEADER hdr;  
78 - u_char buf[MAXPACKET];  
79 -} querybuf_t;  
80 -  
81 -int _stx_dns_ttl;  
82 -  
83 -  
84 -static int parse_answer(querybuf_t *ans, int len, struct in_addr *addrs,  
85 - int *num_addrs)  
86 -{  
87 - char buf[MAXPACKET];  
88 - HEADER *ahp;  
89 - u_char *cp, *eoa;  
90 - int type, n, i;  
91 -  
92 - ahp = &ans->hdr;  
93 - eoa = ans->buf + len;  
94 - cp = ans->buf + sizeof(HEADER);  
95 - h_errno = TRY_AGAIN;  
96 - _stx_dns_ttl = -1;  
97 - i = 0;  
98 -  
99 - while (ahp->qdcount > 0) {  
100 - ahp->qdcount--;  
101 - cp += dn_skipname(cp, eoa) + QFIXEDSZ;  
102 - }  
103 - while (ahp->ancount > 0 && cp < eoa && i < *num_addrs) {  
104 - ahp->ancount--;  
105 - if ((n = dn_expand(ans->buf, eoa, cp, buf, sizeof(buf))) < 0)  
106 - return -1;  
107 - cp += n;  
108 - if (cp + 4 + 4 + 2 >= eoa)  
109 - return -1;  
110 - type = _getshort(cp);  
111 - cp += 4;  
112 - if (type == T_A)  
113 - _stx_dns_ttl = _getlong(cp);  
114 - cp += 4;  
115 - n = _getshort(cp);  
116 - cp += 2;  
117 - if (type == T_A) {  
118 - if (n > sizeof(*addrs) || cp + n > eoa)  
119 - return -1;  
120 - memcpy(&addrs[i++], cp, n);  
121 - }  
122 - cp += n;  
123 - }  
124 -  
125 - *num_addrs = i;  
126 - return 0;  
127 -}  
128 -  
129 -  
130 -static int query_domain(st_netfd_t nfd, const char *name,  
131 - struct in_addr *addrs, int *num_addrs,  
132 - st_utime_t timeout)  
133 -{  
134 - querybuf_t qbuf;  
135 - u_char *buf = qbuf.buf;  
136 - HEADER *hp = &qbuf.hdr;  
137 - int blen = sizeof(qbuf);  
138 - int i, len, id;  
139 -  
140 - for (i = 0; i < _res.nscount; i++) {  
141 - len = res_mkquery(QUERY, name, C_IN, T_A, NULL, 0, NULL, buf, blen);  
142 - if (len <= 0) {  
143 - h_errno = NO_RECOVERY;  
144 - return -1;  
145 - }  
146 - id = hp->id;  
147 -  
148 - if (st_sendto(nfd, buf, len, (struct sockaddr *)&(_res.nsaddr_list[i]),  
149 - sizeof(struct sockaddr), timeout) != len) {  
150 - h_errno = NETDB_INTERNAL;  
151 - /* EINTR means interrupt by other thread, NOT by a caught signal */  
152 - if (errno == EINTR)  
153 - return -1;  
154 - continue;  
155 - }  
156 -  
157 - /* Wait for reply */  
158 - do {  
159 - len = st_recvfrom(nfd, buf, blen, NULL, NULL, timeout);  
160 - if (len <= 0)  
161 - break;  
162 - } while (id != hp->id);  
163 -  
164 - if (len < HFIXEDSZ) {  
165 - h_errno = NETDB_INTERNAL;  
166 - if (len >= 0)  
167 - errno = EMSGSIZE;  
168 - else if (errno == EINTR) /* see the comment above */  
169 - return -1;  
170 - continue;  
171 - }  
172 -  
173 - hp->ancount = ntohs(hp->ancount);  
174 - hp->qdcount = ntohs(hp->qdcount);  
175 - if ((hp->rcode != NOERROR) || (hp->ancount == 0)) {  
176 - switch (hp->rcode) {  
177 - case NXDOMAIN:  
178 - h_errno = HOST_NOT_FOUND;  
179 - break;  
180 - case SERVFAIL:  
181 - h_errno = TRY_AGAIN;  
182 - break;  
183 - case NOERROR:  
184 - h_errno = NO_DATA;  
185 - break;  
186 - case FORMERR:  
187 - case NOTIMP:  
188 - case REFUSED:  
189 - default:  
190 - h_errno = NO_RECOVERY;  
191 - }  
192 - continue;  
193 - }  
194 -  
195 - if (parse_answer(&qbuf, len, addrs, num_addrs) == 0)  
196 - return 0;  
197 - }  
198 -  
199 - return -1;  
200 -}  
201 -  
202 -  
203 -#define CLOSE_AND_RETURN(ret) \  
204 - { \  
205 - n = errno; \  
206 - st_netfd_close(nfd); \  
207 - errno = n; \  
208 - return (ret); \  
209 - }  
210 -  
211 -  
212 -int _stx_dns_getaddrlist(const char *host, struct in_addr *addrs,  
213 - int *num_addrs, st_utime_t timeout)  
214 -{  
215 - char name[MAXDNAME], **domain;  
216 - const char *cp;  
217 - int s, n, maxlen, dots;  
218 - int trailing_dot, tried_as_is;  
219 - st_netfd_t nfd;  
220 -  
221 - if ((_res.options & RES_INIT) == 0 && res_init() == -1) {  
222 - h_errno = NETDB_INTERNAL;  
223 - return -1;  
224 - }  
225 - if (_res.options & RES_USEVC) {  
226 - h_errno = NETDB_INTERNAL;  
227 - errno = ENOSYS;  
228 - return -1;  
229 - }  
230 - if (!host || *host == '\0') {  
231 - h_errno = HOST_NOT_FOUND;  
232 - return -1;  
233 - }  
234 -  
235 - /* Create UDP socket */  
236 - if ((s = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {  
237 - h_errno = NETDB_INTERNAL;  
238 - return -1;  
239 - }  
240 - if ((nfd = st_netfd_open_socket(s)) == NULL) {  
241 - h_errno = NETDB_INTERNAL;  
242 - n = errno;  
243 - close(s);  
244 - errno = n;  
245 - return -1;  
246 - }  
247 -  
248 - maxlen = sizeof(name) - 1;  
249 - n = 0;  
250 - dots = 0;  
251 - trailing_dot = 0;  
252 - tried_as_is = 0;  
253 -  
254 - for (cp = host; *cp && n < maxlen; cp++) {  
255 - dots += (*cp == '.');  
256 - name[n++] = *cp;  
257 - }  
258 - if (name[n - 1] == '.')  
259 - trailing_dot = 1;  
260 -  
261 - /*  
262 - * If there are dots in the name already, let's just give it a try  
263 - * 'as is'. The threshold can be set with the "ndots" option.  
264 - */  
265 - if (dots >= _res.ndots) {  
266 - if (query_domain(nfd, host, addrs, num_addrs, timeout) == 0)  
267 - CLOSE_AND_RETURN(0);  
268 - if (h_errno == NETDB_INTERNAL && errno == EINTR)  
269 - CLOSE_AND_RETURN(-1);  
270 - tried_as_is = 1;  
271 - }  
272 -  
273 - /*  
274 - * We do at least one level of search if  
275 - * - there is no dot and RES_DEFNAME is set, or  
276 - * - there is at least one dot, there is no trailing dot,  
277 - * and RES_DNSRCH is set.  
278 - */  
279 - if ((!dots && (_res.options & RES_DEFNAMES)) ||  
280 - (dots && !trailing_dot && (_res.options & RES_DNSRCH))) {  
281 - name[n++] = '.';  
282 - for (domain = _res.dnsrch; *domain; domain++) {  
283 - strncpy(name + n, *domain, maxlen - n);  
284 - if (query_domain(nfd, name, addrs, num_addrs, timeout) == 0)  
285 - CLOSE_AND_RETURN(0);  
286 - if (h_errno == NETDB_INTERNAL && errno == EINTR)  
287 - CLOSE_AND_RETURN(-1);  
288 - if (!(_res.options & RES_DNSRCH))  
289 - break;  
290 - }  
291 - }  
292 -  
293 - /*  
294 - * If we have not already tried the name "as is", do that now.  
295 - * note that we do this regardless of how many dots were in the  
296 - * name or whether it ends with a dot.  
297 - */  
298 - if (!tried_as_is) {  
299 - if (query_domain(nfd, host, addrs, num_addrs, timeout) == 0)  
300 - CLOSE_AND_RETURN(0);  
301 - }  
302 -  
303 - CLOSE_AND_RETURN(-1);  
304 -}  
305 -  
1 -#include "stx.h"  
2 -#include "common.h"  
3 -  
4 -  
5 -/*****************************************  
6 - * Basic types definitions  
7 - */  
8 -  
9 -struct _stx_centry {  
10 - void *key; /* key for doing lookups */  
11 - void *data; /* data in the cache */  
12 - size_t weight; /* "weight" of this entry */  
13 - struct _stx_centry *next; /* next entry */  
14 - struct _stx_centry **pthis;  
15 - stx_clist_t lru_link; /* for putting this entry on LRU list */  
16 - int ref_count; /* use count for this entry */  
17 - int delete_pending; /* pending delete flag */  
18 -};  
19 -  
20 -struct _stx_cache {  
21 - size_t max_size; /* max size of cache */  
22 - size_t cur_size; /* current size of cache */  
23 -  
24 - size_t max_weight; /* cache capacity */  
25 - size_t cur_weight; /* current total "weight" of all entries */  
26 -  
27 - size_t hash_size; /* size of hash table */  
28 - stx_cache_entry_t **table; /* hash table for this cache */  
29 -  
30 - stx_clist_t lru_list; /* least-recently-used list */  
31 -  
32 - /* Cache stats */  
33 - unsigned long hits; /* num cache hits */  
34 - unsigned long lookups; /* num cache lookups */  
35 - unsigned long inserts; /* num inserts */  
36 - unsigned long deletes; /* num deletes */  
37 -  
38 - /* Functions */  
39 - unsigned long (*key_hash_fn)(const void *);  
40 - long (*key_cmp_fn)(const void *, const void *);  
41 - void (*cleanup_fn)(void *, void *);  
42 -};  
43 -  
44 -  
45 -#define STX_CACHE_ENTRY_PTR(_qp) \  
46 - ((stx_cache_entry_t *)((char *)(_qp) - offsetof(stx_cache_entry_t, lru_link)))  
47 -  
48 -  
49 -/*****************************************  
50 - * Cache methods  
51 - */  
52 -  
53 -stx_cache_t *stx_cache_create(size_t max_size, size_t max_weight,  
54 - size_t hash_size,  
55 - unsigned long (*key_hash_fn)(const void *key),  
56 - long (*key_cmp_fn)(const void *key1,  
57 - const void *key2),  
58 - void (*cleanup_fn)(void *key, void *data))  
59 -{  
60 - stx_cache_t *newcache;  
61 -  
62 - newcache = (stx_cache_t *)calloc(1, sizeof(stx_cache_t));  
63 - if (newcache == NULL)  
64 - return NULL;  
65 - newcache->table = (stx_cache_entry_t **)calloc(hash_size,  
66 - sizeof(stx_cache_entry_t *));  
67 - if (newcache->table == NULL) {  
68 - free(newcache);  
69 - return NULL;  
70 - }  
71 -  
72 - newcache->max_size = max_size;  
73 - newcache->max_weight = max_weight;  
74 - newcache->hash_size = hash_size;  
75 - STX_CLIST_INIT_CLIST(&(newcache->lru_list));  
76 - newcache->key_hash_fn = key_hash_fn;  
77 - newcache->key_cmp_fn = key_cmp_fn;  
78 - newcache->cleanup_fn = cleanup_fn;  
79 -  
80 - return newcache;  
81 -}  
82 -  
83 -  
84 -void stx_cache_empty(stx_cache_t *cache)  
85 -{  
86 - size_t i;  
87 - stx_cache_entry_t *entry, *next_entry;  
88 -  
89 - for (i = 0; i < cache->hash_size; i++) {  
90 - entry = cache->table[i];  
91 - while (entry) {  
92 - next_entry = entry->next;  
93 - stx_cache_entry_delete(cache, entry);  
94 - entry = next_entry;  
95 - }  
96 - }  
97 -}  
98 -  
99 -  
100 -void stx_cache_traverse(stx_cache_t *cache,  
101 - void (*callback)(void *key, void *data))  
102 -{  
103 - size_t i;  
104 - stx_cache_entry_t *entry;  
105 -  
106 - for (i = 0; i < cache->hash_size; i++) {  
107 - for (entry = cache->table[i]; entry; entry = entry->next) {  
108 - if (!entry->delete_pending)  
109 - (*callback)(entry->key, entry->data);  
110 - }  
111 - }  
112 -}  
113 -  
114 -  
115 -void stx_cache_traverse_lru(stx_cache_t *cache,  
116 - void (*callback)(void *key, void *data),  
117 - unsigned int n)  
118 -{  
119 - stx_clist_t *q;  
120 - stx_cache_entry_t *entry;  
121 -  
122 - for (q = STX_CLIST_HEAD(&cache->lru_list); q != &cache->lru_list && n;  
123 - q = q->next, n--) {  
124 - entry = STX_CACHE_ENTRY_PTR(q);  
125 - (*callback)(entry->key, entry->data);  
126 - }  
127 -}  
128 -  
129 -  
130 -void stx_cache_traverse_mru(stx_cache_t *cache,  
131 - void (*callback)(void *key, void *data),  
132 - unsigned int n)  
133 -{  
134 - stx_clist_t *q;  
135 - stx_cache_entry_t *entry;  
136 -  
137 - for (q = STX_CLIST_TAIL(&cache->lru_list); q != &cache->lru_list && n;  
138 - q = q->prev, n--) {  
139 - entry = STX_CACHE_ENTRY_PTR(q);  
140 - (*callback)(entry->key, entry->data);  
141 - }  
142 -}  
143 -  
144 -  
145 -size_t stx_cache_getsize(stx_cache_t *cache)  
146 -{  
147 - return cache->cur_size;  
148 -}  
149 -  
150 -  
151 -size_t stx_cache_getweight(stx_cache_t *cache)  
152 -{  
153 - return cache->cur_weight;  
154 -}  
155 -  
156 -  
157 -void stx_cache_getinfo(stx_cache_t *cache, stx_cache_info_t *info)  
158 -{  
159 - info->max_size = cache->max_size;  
160 - info->max_weight = cache->max_weight;  
161 - info->hash_size = cache->hash_size;  
162 - info->cur_size = cache->cur_size;  
163 - info->cur_weight = cache->cur_weight;  
164 - info->hits = cache->hits;  
165 - info->lookups = cache->lookups;  
166 - info->inserts = cache->inserts;  
167 - info->deletes = cache->deletes;  
168 -}  
169 -  
170 -  
171 -/*****************************************  
172 - * Cache entry methods  
173 - */  
174 -  
175 -stx_cache_entry_t *stx_cache_entry_create(void *key, void *data,  
176 - size_t weight)  
177 -{  
178 - stx_cache_entry_t *newentry;  
179 -  
180 - newentry = (stx_cache_entry_t *)calloc(1, sizeof(stx_cache_entry_t));  
181 - if (newentry == NULL)  
182 - return NULL;  
183 -  
184 - newentry->key = key;  
185 - newentry->data = data;  
186 - newentry->weight = weight;  
187 -  
188 - return newentry;  
189 -}  
190 -  
191 -  
192 -void stx_cache_entry_delete(stx_cache_t *cache, stx_cache_entry_t *entry)  
193 -{  
194 - entry->delete_pending = 1;  
195 -  
196 - if (entry->ref_count > 0)  
197 - return;  
198 -  
199 - if (entry->pthis) {  
200 - *entry->pthis = entry->next;  
201 - if (entry->next)  
202 - entry->next->pthis = entry->pthis;  
203 -  
204 - cache->cur_size--;  
205 - cache->cur_weight -= entry->weight;  
206 - cache->deletes++;  
207 - STX_CLIST_REMOVE_LINK(&(entry->lru_link));  
208 - }  
209 -  
210 - if (cache->cleanup_fn)  
211 - cache->cleanup_fn(entry->key, entry->data);  
212 -  
213 - entry->pthis = NULL;  
214 - entry->key = NULL;  
215 - entry->data = NULL;  
216 - free(entry);  
217 -}  
218 -  
219 -  
220 -stx_cache_entry_t *stx_cache_entry_lookup(stx_cache_t *cache, const void *key)  
221 -{  
222 - unsigned long bucket;  
223 - stx_cache_entry_t *entry;  
224 -  
225 - cache->lookups++;  
226 - bucket = cache->key_hash_fn(key) % cache->hash_size;  
227 - for (entry = cache->table[bucket]; entry; entry = entry->next) {  
228 - if (!entry->delete_pending && cache->key_cmp_fn(key, entry->key) == 0)  
229 - break;  
230 - }  
231 - if (entry) {  
232 - cache->hits++;  
233 - if (entry->ref_count == 0)  
234 - STX_CLIST_REMOVE_LINK(&(entry->lru_link));  
235 - entry->ref_count++;  
236 - }  
237 -  
238 - return entry;  
239 -}  
240 -  
241 -  
242 -void stx_cache_entry_release(stx_cache_t *cache, stx_cache_entry_t *entry)  
243 -{  
244 - if (entry->ref_count == 0)  
245 - return;  
246 -  
247 - entry->ref_count--;  
248 -  
249 - if (entry->ref_count == 0) {  
250 - STX_CLIST_APPEND_LINK(&(entry->lru_link), &(cache->lru_list));  
251 - if (entry->delete_pending)  
252 - stx_cache_entry_delete(cache, entry);  
253 - }  
254 -}  
255 -  
256 -  
257 -int stx_cache_entry_insert(stx_cache_t *cache, stx_cache_entry_t *entry)  
258 -{  
259 - stx_cache_entry_t *old_entry;  
260 - unsigned long bucket;  
261 -  
262 - /*  
263 - * If cache capacity is exceeded, try to remove LRU entries till there is  
264 - * enough room or LRU list is empty.  
265 - */  
266 - while (cache->cur_weight + entry->weight > cache->max_weight) {  
267 - old_entry = stx_cache_entry_getlru(cache);  
268 - if (!old_entry) {  
269 - /* cache capacity is exceeded and all entries are in use */  
270 - return -1;  
271 - }  
272 - stx_cache_entry_delete(cache, old_entry);  
273 - }  
274 -  
275 - /* If cache size is exceeded, remove LRU entry */  
276 - if (cache->cur_size >= cache->max_size) {  
277 - old_entry = stx_cache_entry_getlru(cache);  
278 - if (!old_entry) {  
279 - /* cache size is exceeded and all entries are in use */  
280 - return -1;  
281 - }  
282 - stx_cache_entry_delete(cache, old_entry);  
283 - }  
284 -  
285 - /* Don't add duplicate entries in the cache */  
286 - bucket = cache->key_hash_fn(entry->key) % cache->hash_size;  
287 - for (old_entry = cache->table[bucket]; old_entry;  
288 - old_entry = old_entry->next) {  
289 - if (!old_entry->delete_pending &&  
290 - cache->key_cmp_fn(entry->key, old_entry->key) == 0)  
291 - break;  
292 - }  
293 - if (old_entry)  
294 - stx_cache_entry_delete(cache, old_entry);  
295 -  
296 - /* Insert in the hash table */  
297 - entry->next = cache->table[bucket];  
298 - cache->table[bucket] = entry;  
299 - entry->pthis = &cache->table[bucket];  
300 - if (entry->next)  
301 - entry->next->pthis = &entry->next;  
302 - entry->ref_count++;  
303 -  
304 - cache->inserts++;  
305 - cache->cur_size++;  
306 - cache->cur_weight += entry->weight;  
307 -  
308 - return 0;  
309 -}  
310 -  
311 -  
312 -stx_cache_entry_t *stx_cache_entry_getlru(stx_cache_t *cache)  
313 -{  
314 - if (STX_CLIST_IS_EMPTY(&(cache->lru_list)))  
315 - return NULL;  
316 -  
317 - return STX_CACHE_ENTRY_PTR(STX_CLIST_HEAD(&(cache->lru_list)));  
318 -}  
319 -  
320 -  
321 -int stx_cache_entry_sizeof(void)  
322 -{  
323 - return (int)sizeof(stx_cache_entry_t);  
324 -}  
325 -  
326 -  
327 -void *stx_cache_entry_getdata(stx_cache_entry_t *entry)  
328 -{  
329 - return entry->data;  
330 -}  
331 -  
332 -  
333 -void *stx_cache_entry_getkey(stx_cache_entry_t *entry)  
334 -{  
335 - return entry->key;  
336 -}  
337 -  
338 -  
339 -size_t stx_cache_entry_getweight(stx_cache_entry_t *entry)  
340 -{  
341 - return entry->weight;  
342 -}  
343 -