Patrick,

Patrick wrote:
> About forking and NSS:

I have to stop your right here :

NSS is much better suited for multithreaded applications than 
multiprocess applications. Multiprocess applications are supported, as 
well as hybrid multiprocess / multithreaded applications, but they are a 
lot more work to implement.

An NSS multiprocess server requires a very particular architecture. NSS 
should never be initialized in a process before a fork() call due to the 
undefined behavior of a PKCS#11 module after a fork. In general NSS

The softoken which handles the cert and key databases is a PKCS#11 
module, and it is not safe for forking, even if NSS is initialized for 
read-only operation. This is because when the softoken is initialized, 
it opens database files. In Unix, there is a single underlying file 
descriptor context for that handle when you fork. So if you do, and your 
child processes all do I/O do the database at the same time, such as to 
look for a certificate or a CRL, they will do read() and seek() on the 
same fd, causing unpredictable results. The only way to solve the 
problem is to have each child process open separate files, read-only, 
and this can only be done by delaying the NSS_Initialize call until 
after the fork() call.

Hardware PKCS#11 modules are even worse WRT fork() safety, and will 
almost never work correctly after a fork. The reason is very similar to 
why softoken won't work - the PKCS#11 vendor library open a file handle 
to their physical device driver, and then the file handle gets shared 
between the child processes, causing a mess.

For NSS to work with forking, the parent process must call
SSL_ConfigMPServerSIDCache once . It is then free to fork().

Child processes must then each call SSL_InheritMPServerSIDCache() , then 
NSS_Initialize().

Typically, the parent process will creates the listen socket, and 
initialize the multiprocess SSL server cache . It will then fork a 
number of child server processes.

Each child will then initialize NSS and accept connections from the 
listen socket. You have several choices there.

1) convert the listen socket to SSL, using SSL_ImportFD and the other 
SSL calls. This is one-time overhead

2) convert accepted sockets to SSL . This will be n-time overhead, and 
will only work well if you use connection, I highly recommend that you 
use a model socket to do so, otherwise it will be very slow because you 
have to keep making a bunch of other setup calls on each accepted socket.

> How best does one use NSS to do create an app that accepts connection
> requests and forking off children to handle them? That is, how best to do
> the following but with NSS:
> 
> /*
>    * Loop forever accepting connection requests and forking off
>    * children to handle them.
>    */
>   while (1) {
>    addrlen = sizeof(his_addr);
>    fd2 = accept(ctl_sock, (struct sockaddr *)&his_addr, &addrlen);
>    if (fork() == 0) {
>     /* child */
>     (void) dup2(fd2, 0);
>     (void) dup2(fd2, 1);
>     close(ctl_sock);
>     break;
>    }
>    close(fd2);
>   }
> 
> I don't see the equivalent to dup2 in NSS,  no way to duplicate an NSPR file
> descriptor... Another solution could be to import the new fd2 into NSPR/NSS:
> I thought that at one time NSS had a function for import an exiting FD into
> NSPR. Is it gone?

There is an NSPR function to convert a native fd to a PRFileDesc, called 
  PR_FileDesc2NativeHandle , in private/pprio.h . Since it is in a 
private header file, this means that API is subject to change, but for 
now it can be used.

As far as your program above, to get it working from a strictly 
functional point of view, all you need to do is add a call to 
SSL_ConfigMPServerSIDCache before your while loop, and in your "child" 
block of code, add calls to SSL_ConfigInheritMPServerSIDCache , 
NSS_Initialize, PR_FileDesc2NativeHandle, and SSL_ImportFD . Actually, 
there are a few more NSS calls needed after SSL_ImportFD, but you get 
the idea.

You will find that this will work, but it will perform extremely poorly 
because NSS will be reinitialized each time a connection comes in. This 
is the wrong program architecture for NSS. You need to prefork the 
number of processes you need, so they can each initialize NSS and incur 
the setup overhead first, and then have them accept connections, either 
in a loop with PR_Accept, or better yet, by making each of your 
processes multithreaded. The later is a hybrid multiprocess / 
multithreaded architecture, and gives the best performance, but it is 
also the hardest to implement. I was part of a team of Sun and Netscape 
engineers that spent years optimizing the code for a multiprocess 
multithreaded SSL server using NSS. The result of our efforts are in the 
latest 6.x versions of iPlanet Web Server / Netscape Enterprise Server.

-- 
"Except for the lack of debugging and the ps thing, [Linux] kernel
threads are generally fine right now. And if you're not too fussed
about the more fiddly details of POSIX threads, and your application
doesn't spend most of its time in thread creation, then LinuxThreads
is great too."

   Linux-Kernel archive


Reply via email to