7.2. The takedescriptor() and givedescriptor() API calls

The givedescriptor() and takedescriptor() API calls are vital for implementing a server using the 'job spawning approach.' They allow a listener program to pass a descriptor to a server instance program.

At first glance, it sounds as if this should be easy. After all, a socket descriptor is just a number, right? And passing a number from one program to another isn't that hard, is it?

Well, unfortunately it's not that easy. First of all, there's security. Understandably, you don't want one job to be able to read data from a socket that another job has open! If that were allowed, I could write a simple loop to try every descriptor on the system and peek into the affairs of every network job on the system! Yikes!

Secondly, each open descriptor is an attribute of the job it was opened in. Job A and Job B can both have a descriptor #5. And they can be different things. The memory that the system uses to keep track of each descriptor is allocated to each individual job. If it weren't, then jobs could interfere with each other -- and possibly crash one another. While this may be acceptable in Windows, it certainly is not in OS/400!

So, in order to pass a descriptor from one job to another, you MUST use the givedescriptor() and takedescriptor() APIs.

The givedescriptor() API is documented in the IBM manual page at this location: http://publib.boulder.ibm.com/pubs/html/as400/v4r5/ic2924/info/apis/gvsoc.htm

This manual lists the C language prototype for the givedescriptor() API as looking like this:

                int givedescriptor(int descriptor, char *target_job);

Pretty straight forward, right? It's called 'givedescriptor', and it returns an integer. It has two parameters, the first one is an integer, and the second one is a pointer.

To make the same prototype in RPG, we add this to our SOCKET_H member:

         D givedescriptor  PR            10I 0 extproc('givedescriptor')      
         D   SockDesc                    10I 0 VALUE                          
         D   target_job                    *   VALUE                          

The "usage notes" tell us that the "target_job" parameter is the 'internal job identifier' of the job that we want to give the descriptor to. And that we can look up this job identifier using the QUSRJOBI API.

The IBM manual page for the takedescriptor() API is located here: http://publib.boulder.ibm.com/pubs/html/as400/v4r5/ic2924/info/apis/tksoc.htm

And it tells us that the C language prototype for the takedescriptor() API looks like this:

                 int takedescriptor(char *source_job);

So, the API is called 'takedescriptor'. It returns an integer, and accepts one parameter, a pointer. This makes the RPG prototype look like this:

         D takedescriptor  PR            10I 0 extproc('takedescriptor')       
         D   source_job                    *   VALUE

So add that prototype to your SOCKET_H member as well.

The usage notes for the takedescriptor() API tell us that the source_job is also the 'internal job identifier' which we can retrieve using the QUSRJOBI API. It also tells us that if we set the source_job parameter to NULL, it'll take any descriptor that the system tries to pass to it.

You can call these APIs in RPG by doing something like this:

In the "listener" program:

         c                   if        givedescriptor(sock: target_int_id) < 0  
         c*** handle failure                                                    
         c                   endif                                              

And in the "server instance" program:

         c                   eval      sock = takedescriptor(*NULL)             

The tricky part, of course, is getting the 'target_int_id', which we shall discuss in the next section.