To facilitate the development of ATMI servers, the Oracle Tuxedo system provides a predefined 
main() routine for server load modules. When you execute the 
buildserver command, the 
main() routine is automatically included as part of the server.
 
      
        
          
            | 
                Note:	
               | The main() routine that the system provides is a closed abstraction; you cannot modify it. | 
        
       
      
      
      
      
      
      
      
      
        
          
            | • | Calls the function tpsvrinit() to process any command-line arguments listed after the double dash (-- ) and optionally to open the resource manager. These command-line arguments are used for application-specific initialization. | 
        
       
      
      
      
        
          
            | • | If the -r option is specified, records the starting time of the service request. | 
        
       
      
      
      
      
        
          
            | • | If the -r option is specified, records the ending time of the service request. | 
        
       
      
      
      
      
      As indicated above, the main() routine handles all of the details associated with joining and exiting from an application, managing buffers and transactions, and handling communication.
 
      
      
      The main() routine provides one system-supplied ATMI server, 
AUTHSVR, and two subroutines, 
tpsvrinit() and 
tpsvrdone(). The default versions of all three, which are described in the following sections, can be modified to suit your application.
 
      
        
          
            | 
                Notes:	
               | If you want to write your own versions of tpsvrinit() and tpsvrdone() , remember that the default versions of these two routines call tx_open()  and tx_close() , respectively. If you write a new version of tpsvrinit()  that calls tpopen()  rather than tx_open() , you should also write a new version of tpsvrdone()  that calls tpclose() . In other words, both functions in an open/close pair must belong to the same set. | 
        
       
      
      
      You can use the AUTHSVR(5) server to provide individual client authentication for an application. The 
tpinit() function calls this server when the level of security for the application is 
TPAPPAUTH.
 
      The service in AUTHSVR looks in the 
data field of the 
TPINIT buffer for a user password (not to be confused with the application password specified in the 
passwd field of the 
TPINIT buffer). By default, the system takes the string in 
data and searches for a matching string in the 
/etc/passwd file.
 
      When called by a native-site client, tpinit() forwards the 
data field as it is received. This means that if the application requires the password to be encrypted, the client program must be coded accordingly. 
 
      
      
      When a server is booted, the Oracle Tuxedo system 
main() calls 
tpsvrinit(3c) during its initialization phase, before handling any service requests.
 
      
      You can use the tpsvrinit() function for any initialization processes that might be required by an application, such as the following:
 
      
      
      
      
      
      int
tpsvrinit(int argc, char **argv)
 
      
      
      Listing 5‑1 shows how the 
tpsvrinit() function is used to receive command-line options.
 
      
      tpsvrinit(argc, argv)
int argc;
char **argv;
{
        int c;
        extern char *optarg;
        extern int optind;
        .
        .
        .
        while((c = getopt(argc, argv, "f:x:")) != EOF)
            switch(c){
            .
            .
            .
            } 
        .
        .
        .
}
 
      
      When main() calls 
tpsvrinit(), it picks up any arguments that follow the double dash (
--) on the command line. In the example above, options 
f and 
x each takes an argument, as indicated by the colon. 
optarg points to the beginning of the option argument. The switch statement logic is omitted.
 
      
      The following example illustrates another common use of tpsvrinit(): opening a resource manager. The Oracle Tuxedo system provides functions to open a resource manager, 
tpopen(3c) and 
tx_open(3c). It also provides the complementary functions, 
tpclose(3c) and 
tx_close(3c). Applications that use these functions to open and close their resource managers are portable in this respect. They work by accessing the resource manager instance-specific information that is available in the configuration file.
 
      
      
      
      
      tpsvrinit()
{
 
    /* Open database */ 
    if (tpopen() == -1) {
          (void)userlog("tpsvrinit: failed to open database: ");
          switch (tperrno) {
             case TPESYSTEM:
                 (void)userlog("System error\n");
                 break;
             case TPEOS:
                 (void)userlog("Unix error %d\n",Uunixerr);
                 break;
             case TPEPROTO:
                 (void)userlog("Called in improper context\n");
                 break;
             case TPERMERR:
                 (void)userlog("RM failure\n");
                 break;
       }
       return(-1);     /* causes the server to exit */
   }
  return(0);
}
 
      
      
      
      The tpsvrdone() function calls 
tpclose() to close the resource manager, similarly to the way 
tpsvrinit() calls 
tpopen() to open it. 
 
      
      
      void
tpsvrdone()  /* Server termination routine */
 
      The tpsvrdone() function requires no arguments.
 
      If an application does not define a closing routine for tpsvrdone(), the Oracle Tuxedo system calls the default routine supplied by 
main(). This routine calls 
tx_close() and 
userlog() to close the resource manager and write to the central event log, respectively. The message sent to the log indicates that the server is about to exit.
 
      tpsvrdone() is called after the server has finished processing service requests but before it exits. Because the server is still part of the system, further communication and transactions can take place within the routine, as long as certain rules are followed. These rules are covered in 
“Managing Errors” on page 11‑1. 
 
      Listing 5‑3 illustrates how to use the 
tpsvrdone() function to close a resource manager and exit gracefully.
 
      
      
      
      
      Because the communication details are handled by the Oracle Tuxedo system main() routine, you can concentrate on the application service logic rather than communication implementation. For compatibility with the system-supplied 
main(), however, application services must adhere to certain conventions. These conventions are referred to, collectively, as the service template for coding service routines. They are summarized in the following list. Refer to the 
tpservice(3c) reference page in the 
Oracle Tuxedo ATMI C Function Reference for more information on these conventions.
 
      
      
      
      
      
      
      
      char      name[32];
long      
flags;
char      *
data; 
long      
len;
int       
cd;
int       
appkey;
CLIENTID  
cltid;
 
      Table 5‑1 summarizes the 
TPSVCINFO data structure.
 
      
      
        
          
        
        
          |  |  | 
        
          |  |  | 
        
          |  | The TPTRAN flag indicates that the service is in transaction mode. When a service is invoked through a call to tpcall()  or tpacall()  with the flags  parameter set to TPNOTRAN , the service cannot participate in the current transaction. However, it is still possible for the service to be executed in transaction mode. That is, even when the caller sets the TPNOTRAN  communication flag, it is possible for TPTRAN  to be set in svcinfo->flags . For an example of such a situation, refer to “Writing Global Transactions” on page 9‑1 .  The flags member is set to TPNOREPLY  if the service is called by tpacall()  with the TPNOREPLY  communication flag set. If a called service is part of the same transaction as the calling process, it must return a reply to the caller. | 
        
          |  | Pointer to a buffer that was previously allocated by tpalloc() within the main() . This buffer is used to receive request messages. However, it is recommended that you also use this buffer to send back reply messages or forward request messages. | 
        
          |  |  | 
        
          |  |  | 
        
          |  | Reserved for use by the application. If application-specific authentication is part of your design, the application-specific authentication server, which is called when a client joins the application, should return a client authentication key as well as an indication of success or failure. The Oracle Tuxedo system holds the appkey on behalf of the client and passes the information to subsequent service requests in this field. By the time the appkey  is passed to a service, the client has already been authenticated. However, the appkey  field can be used within a service to identify the user invoking the service or some other parameters associated with the user. | 
        
          |  | Structure of type CLIENTID used by the system to carry the identification of the client. You should not modify this structure. | 
      
      When the data field in the 
TPSVCINFO structure is being accessed by a process, the following buffer types must agree:
 
      
      
      
      Listing 5‑4 illustrates a typical service definition. This code is borrowed from the 
ABAL (account balance) service routine that is part of the banking application provided with the Oracle Tuxedo software. 
ABAL is part of the 
BAL server.
 
      
      #include <stdio.h>      /* UNIX */
#include <atmi.h>       /* BEA Tuxedo System */
#include <sqlcode.h>    /* BEA Tuxedo System */
#include "bank.flds.h"  /* bankdb fields */
#include "aud.h"        /* BANKING view defines */
 
EXEC SQL begin declare section;
static long branch_id;  /* branch id */
static float bal;       /* balance */
EXEC SQL end declare section;
 
/*
 * Service to find sum of the account balances at a SITE
 */
 
void
#ifdef __STDC__
ABAL(TPSVCINFO *transb)
 
#else
 
ABAL(transb)
TPSVCINFO *transb;
#endif
 
{
     struct aud *transv;         /* view of decoded message */
 
     /* Set pointer to TPSVCINFO data buffer */
 
     transv = (struct aud *)transb->data;
 
     set the consistency level of the transaction
      /* Get branch id from message, do query */ 
     EXEC SQL declare acur cursor for
      select SUM(BALANCE) from ACCOUNT;
     EXEC SQL open acur;         /* open */
     EXEC SQL fetch acur into :bal;   /* fetch */
     if (SQLCODE != SQL_OK) {      /* nothing found */
      (void)strcpy (transv->ermsg,"abal failed in sql aggregation"); 
      EXEC SQL close acur;
      tpreturn(TPFAIL, 0, transb->data, sizeof(struct aud), 0);
     }
     EXEC SQL close acur;
     transv->balance = bal;
     tpreturn (TPSUCCESS, 0, transb->data, sizeof(struct aud), 0);
}
 
      
      In the preceding example, the application allocates a request buffer on the client side by a call to tpalloc() with the 
type parameter set to 
VIEW and the 
subtype set to 
aud. The 
ABAL service is defined as supporting the 
VIEW typed buffer. The 
BUFTYPE parameter is not specified for 
ABAL and defaults to 
ALL. The 
ABAL service allocates a buffer of the type 
VIEW and assigns the 
data member of the 
TPSVCINFO structure that was passed to the 
ABAL subroutine to the buffer pointer. The 
ABAL server retrieves the appropriate data buffer by accessing the corresponding 
data member, as illustrated in the preceding example.
 
      
      
      
      This example is derived from the ABAL service that is part of the banking application provided with the Oracle Tuxedo software. It shows how the service is written to accept a request either as an 
aud VIEW or an 
FML buffer. If its attempt to determine the message type fails, the service returns a string with an error message plus an appropriate return code; otherwise it executes the segment of code that is appropriate for the buffer type. For more information on the 
tpreturn() function, refer to 
“Terminating a Service Routine” on page 5‑16.
 
      
      #define TMTYPERR 1 /* return code indicating tptypes failed */
#define INVALMTY 2 /* return code indicating invalid message type */
 
void
ABAL(transb)
 
TPSVCINFO *transb;
 
{
   struct aud *transv; /* view message */
   FBFR *transf;  /* fielded buffer message */
   int repc;  /* tpgetrply return code */
   char typ[TMTYPELEN+1], subtyp[TMSTYPELEN+1]; /* type, subtype of message */
   char *retstr;  /* return string if tptypes fails */
 
/* find out what type of buffer sent */ 
   if (tptypes((char *)transb->data, typ, subtyp) == -1) {
      retstr=tpalloc("STRING", NULL, 100);
      (void)sprintf(retstr,
      "Message garbled; tptypes cannot tell what type message\n");
      tpreturn(TPFAIL, TMTYPERR, retstr, 100, 0);
   }
/* Determine method of processing service request based on type */
   if (strcmp(typ, "FML") == 0) {
      transf = (FBFR *)transb->data;
... code to do abal service for fielded buffer ...
 tpreturn succeeds and sends FML buffer in reply
   }
   else if (strcmp(typ, "VIEW") == 0 && strcmp(subtyp, "aud") == 0) {
      transv = (struct aud *)transb->data;
... code to do abal service for aud struct ...
 tpreturn succeeds and sends aud view buffer in reply
   }
   else {
      retstr=tpalloc("STRING", NULL, 100);
      (void)sprintf(retstr,
      "Message garbled; is neither FML buffer nor aud view\n");
      tpreturn(TPFAIL, INVALMTY, retstr, 100, 0);
   }
}
 
      
      
      
      
      
      
      #include <stdio.h>
#include "atmi.h"
 
char *roundrobin();
 
PRINTER(pbuf)
 
TPSVCINFO *pbuf;     /* print buffer */
 
{
char prname[20], ocmd[30];     /* printer name, output command */
long rlen;                     /* return buffer length */
int prio;                      /* priority of request */
FILE *lp_pipe;                 /* pipe file pointer */
 
prio=tpgprio();
if (prio <= 20)
   (void)strcpy(prname,"bigjobs"); /* send low priority (verbose)
                                     jobs to big comp. center
                                     laser printer where operator
                                     sorts output and puts it
                                     in a bin */
else if (prio <= 60)
   (void)strcpy(prname,roundrobin()); /* assign printer on a
                                        rotating basis to one of
                                        many local small laser printers
                                        where output can be picked
                                        up immediately; roundrobin() cycles
                                        through list of printers */
else
   (void)strcpy(prname,"hispeed");
                                   /* assign job to high-speed laser
                                      printer; reserved for those who
                                      need verbose output on a daily,
                                      frequent basis */
 
(void)sprintf(ocmd, "lp -d%s", prname);   /* output lp(1) command */
lp_pipe = popen(ocmd, "w");               /* create pipe to command */
(void)fprintf(lp_pipe, "%s", pbuf->data); /* print output there */
(void)pclose(lp_pipe);                    /* close pipe */
 
if ((pbuf->flags & TPNOREPLY))
   tpreturn(TPSUCCESS, 0, NULL, 0, 0);
rlen = strlen(prname) + 1;
pbuf->data = tprealloc(pbuf->data, rlen); /* ensure enough space for name */
(void)strcpy(pbuf->data, prname);
tpreturn(TPSUCCESS, 0, pbuf->data, rlen, 0);
 
char *
roundrobin()
 
{
static char *printers[] = {"printer1", "printer2", "printer3", "printer4"};
static int p = 0;
 
if (p > 3)
   p=0;
return(printers[p++]);
} 
      
      
      
      
      
      
        
          
            | • | tpforward() forwards a request to another service for further processing. | 
        
       
      
      The tpreturn(3c) function marks the end of the service routine and sends a message to the requester. Use the following signature to call the 
tpreturn() function:
 
      void
tpreturn(int rval, int rcode, char *data, long len, long flags)
 
      Table 5‑2 describes the arguments to the 
tpreturn() function.
 
      
      
        
          
        
        
          |  |  | 
        
          |  | 
              
                
                  | •	 | TPSUCCESS—the calling function succeeded. The function stores the reply message in the caller’s buffer. If there is a reply message, it is in the caller’s buffer.  |  
              
                
                  | •	 | TPFAIL (default)—the service terminated unsuccessfully. The function reports an error message to the client process waiting for the reply. In this case, the client’s tpcall()  or tpgetrply()  function call fails and the system sets the tperrno(5)  variable to TPESVCFAIL  to indicate an application-defined failure. If a reply message was expected, it is available in the caller’s buffer. |  
              
                
                  | •	 | TPEXIT—the service terminated unsuccessfully. The function reports an error message to the client process waiting for the reply, and exits. |  | 
        
          |  |  | 
        
          |  | If you use the same buffer that was passed to the service in the SVCINFO structure, you need not be concerned with buffer allocation or disposition because both are handled by the system-supplied main() . You cannot free this buffer using the tpfree()  command; any attempt to do so quietly fails. You can resize the buffer using the tprealloc()  function.  | 
        
          |  | If a reply is expected by the client and there is no data in the reply buffer (that is, if the data argument is set to the NULL pointer), the function sends a reply with zero length, without modifying the client’s buffer.  | 
        
          |  |  | 
      
      
      
      When tpreturn() is called, control always returns to 
main(). If a service has sent requests with asynchronous replies, it must receive all expected replies or invalidate them with 
tpcancel() before returning control to 
main(). Otherwise, the outstanding replies are automatically dropped when they are received by the Oracle Tuxedo system 
main(), and an error is returned to the caller.
 
      
      If a reply is expected and tpreturn() encounters errors while processing its arguments, it sends a 
failed message to the calling process. The caller detects the error by checking the value placed in 
tperrno. In the case of failed messages, the system sets 
tperrno to 
TPESVCERR. This situation takes precedence over the value of the 
tpurcode global variable. If this type of error occurs, no reply data is returned, and both the contents and length of the caller’s output buffer remain unchanged.
 
      If tpreturn() returns a message in a buffer of an unknown type or a buffer that is not allowed by the caller (that is, if the call is made with 
flags set to 
TPNOCHANGE), the system returns 
TPEOTYPE in 
tperrno(5). In this case, application success or failure cannot be determined, and the contents and length of the output buffer remain unchanged.
 
      The value returned in the tpurcode(5) global variable is not relevant if the 
tpreturn() function is invoked and a timeout occurs for the call waiting for the reply. This situation takes precedence over all others in determining the value that is returned in 
tperrno(5). In this case, 
tperrno(5) is set to 
TPETIME and the reply data is not sent, leaving the contents and length of the caller’s reply buffer unchanged. There are two types of timeouts in the Oracle Tuxedo system: blocking and transaction timeouts (discussed in 
“Writing Global Transactions” on page 9‑1).
 
      The example code in this section shows the TRANSFER service that is part of the 
XFER server. Basically, the 
TRANSFER service makes synchronous calls to the 
WITHDRAWAL and 
DEPOSIT services. It allocates a separate buffer for the reply message since it must use the request buffer for the calls to both the 
WITHDRAWAL and the 
DEPOSIT services. If the call to 
WITHDRAWAL fails, the service writes the message 
cannot withdraw on the status line of the form, frees the reply buffer, and sets the 
rval argument of the 
tpreturn() function to 
TPFAIL. If the call succeeds, the debit balance is retrieved from the reply buffer.
 
      
      A similar scenario is followed for the call to DEPOSIT. On success, the service frees the reply buffer that was allocated in the service routine and sets the 
rval argument to 
TPSUCCESS, returning the pertinent account information to the status line.
 
      
      #include <stdio.h>      /* UNIX */
#include <string.h>     /* UNIX */
#include "fml.h"        /* BEA Tuxedo System */
#include "atmi.h"       /* BEA Tuxedo System */
#include "Usysflds.h"   /* BEA Tuxedo System */
#include "userlog.h"    /* BEA Tuxedo System */
#include "bank.h"       /* BANKING #defines */
#include "bank.flds.h"  /* bankdb fields */
 
 
/*
 * Service to transfer an amount from a debit account to a credit 
 * account
 */
 
void
#ifdef __STDC__
TRANSFER(TPSVCINFO *transb)
 
#else
 
TRANSFER(transb)
TPSVCINFO *transb;
#endif
 
{
   FBFR *transf;          /* fielded buffer of decoded message */
   long db_id, cr_id;     /* from/to account id’s              */
   float db_bal, cr_bal;  /* from/to account balances          */
   float tamt;            /* amount of the transfer            */
   FBFR *reqfb;           /* fielded buffer for request message*/
   int reqlen;            /* length of fielded buffer          */
   char t_amts[BALSTR];   /* string for transfer amount        */
   char db_amts[BALSTR];  /* string for debit account balance  */
   char cr_amts[BALSTR];  /* string for credit account balance */
 
/* Set pointr to TPSVCINFO data buffer */
transf = (FBFR *)transb->data;
 
/* Get debit (db_id) and credit (cr_id) account IDs */
 
/* must have valid debit account number */
if (((db_id = Fvall(transf, ACCOUNT_ID, 0)) < MINACCT) || (db_id > MAXACCT)) {
   (void)Fchg(transf, STATLIN, 0,"Invalid debit account number",(FLDLEN)0);
   tpreturn(TPFAIL, 0, transb->data, 0L, 0);
}
/* must have valid credit account number */
if ((cr_id = Fvall(transf, ACCOUNT_ID, 1)) < MINACCT || cr_id > MAXACCT) {
   (void)Fchg(transf,STATLIN, 0,"Invalid credit account number",(FLDLEN)0);
   tpreturn(TPFAIL, 0, transb->data, 0L, 0);
}
 
/* get amount to be withdrawn */
if (Fget(transf, SAMOUNT, 0, t_amts, <  0) 0 || strcmp(t_amts,"") == 0) {
   (void)Fchg(transf, STATLIN, 0, "Invalid amount",(FLDLEN)0);
   tpreturn(TPFAIL, 0, transb->data, 0L, 0);
}
(void)sscanf(t_amts,"%f",tamt);
 
/* must have valid amount to transfer */
if (tamt = 0.0) {
   (void)Fchg(transf, STATLIN, 0,
      "Transfer amount must be greater than $0.00",(FLDLEN)0);
    tpreturn(TPFAIL, 0, transb->data, 0L, 0);
}
 
/* make withdraw request buffer */
if ((reqfb = (FBFR *)tpalloc("FML",NULL,transb->len)) == (FBFR *)NULL) {
   (void)userlog("tpalloc failed in transfer\n");
   (void)Fchg(transf, STATLIN, 0,
      "unable to allocate request buffer", (FLDLEN)0);
   tpreturn(TPFAIL, 0, transb->data, 0L, 0);
}
reqlen = Fsizeof(reqfb);
 
/* put ID in request buffer */
(void)Fchg(reqfb,ACCOUNT_ID,0,(char *)&db_id, (FLDLEN)0);
 
/* put amount in request buffer */
(void)Fchg(reqfb,SAMOUNT,0,t_amts, (FLDLEN)0);
 
/* increase the priority of withdraw call */
if (tpsprio(PRIORITY, 0L) == -1)
   (void)userlog("Unable to increase priority of withdraw\n");
 
if (tpcall("WITHDRAWAL", (char *)reqfb,0, (char **)&reqfb,
      (long *)&reqlen,TPSIGRSTRT) == -1) {
   (void)Fchg(transf, STATLIN, 0,
         "Cannot withdraw from debit account", (FLDLEN)0);
   tpfree((char *)reqfb);
   tpreturn(TPFAIL, 0,transb->data, 0L, 0);
}
 
/* get "debit" balance from return buffer */
 
(void)strcpy(db_amts, Fvals((FBFR *)reqfb,SBALANCE,0));
void)sscanf(db_amts,"%f",db_bal);
if ((db_amts == NULL) || (db_bal < 0.0)) {
   (void)Fchg(transf, STATLIN, 0,
         "illegal debit account balance", (FLDLEN)0);
   tpfree((char *)reqfb);
   tpreturn(TPFAIL, 0, transb->data, 0L, 0);
}
 
/* put deposit account ID in request buffer */
(void)Fchg(reqfb,ACCOUNT_ID,0,(char *)&cr_id, (FLDLEN)0);
 
/* put transfer amount in request buffer */
(void)Fchg(reqfb,SAMOUNT,0,t_amts, (FLDLEN)0);
 
/* Up the priority of deposit call */
if (tpsprio(PRIORITY, 0L) == -1)
     (void)userlog("Unable to increase priority of deposit\n");
 
/* Do a tpcall to deposit to second account */
if (tpcall("DEPOSIT", (char *)reqfb, 0, (char **)&reqfb,
        (long *)&reqlen, TPSIGRSTRT) == -1) {
   (void)Fchg(transf, STATLIN, 0,
           "Cannot deposit into credit account", (FLDLEN)0);
   tpfree((char *)reqfb);
   tpreturn(TPFAIL, 0,transb->data, 0L, 0);
}
 
/* get "credit" balance from return buffer */
 
(void)strcpy(cr_amts, Fvals((FBFR *)reqfb,SBALANCE,0));
(void)sscanf(cr_amts,"%f",&cr_bal);
if ((cr_amts == NULL) || (cr_bal 0.0)) {
   (void)Fchg(transf, STATLIN, 0,
         "Illegal credit account balance", (FLDLEN)0);
    tpreturn(TPFAIL, 0, transb->data, 0L, 0);
}
 
/* set buffer for successful return */
(void)Fchg(transf, FORMNAM, 0, "CTRANSFER", (FLDLEN)0);
(void)Fchg(transf, SAMOUNT, 0, Fvals(reqfb,SAMOUNT,0), (FLDLEN)0);
(void)Fchg(transf, STATLIN, 0, "", (FLDLEN)0);
(void)Fchg(transf, SBALANCE, 0, db_amts, (FLDLEN)0);
(void)Fchg(transf, SBALANCE, 1, cr_amts, (FLDLEN)0);
tpfree((char *)reqfb);
tpreturn(TPSUCCESS, 0,transb->data, 0L, 0);
} 
      
      
      
      
      
      The cd (call descriptor) argument identifies the process you want to cancel.
 
      tpcancel() cannot be used for transaction replies (that is, for replies to requests made without the 
TPNOTRAN flag set). Within a transaction, 
tpabort(3c) does the same job of invalidating the transaction call descriptor. 
 
      
      
      int cd1;
 .
 .
 .
      if ((cd1=tpacall(sname, (char *)audv, sizeof(struct aud),
         TPNOTRAN)) == -1) {
      .
      .
      .
      }
      if (tpgetrply(cd1, (char **)&audv,&audrl, 0) == -1) {
      if (tperrno == TPETIME) {
          tpcancel(cd1);
          .
          .
          .
         }
      }
      tpreturn(TPSUCCESS, 0,NULL, 0L, 0); 
      
      
      The tpforward(3c) function allows a service to forward a request to another service for further processing. 
 
      
      void
tpforward(char *svc, char *data, long len, long flags)
 
      Table 5‑3 describes the arguments to the 
tpreturn() function.
 
      
      
      The functionality of tpforward() differs from a service call: a service that forwards a request does not expect a reply. The responsibility for providing the reply is passed to the service to which the request has been forwarded. The latter service sends the reply to the process that originated the request. It becomes the responsibility of the last server in the forward chain to send the reply to the originating client by invoking 
tpreturn().
 
      Figure 5‑1 shows one possible sequence of events when a request is forwarded from one service to another. Here a client initiates a request using the 
tpcall() function and the last service in the chain (
SVC_C) provides a reply using the 
tpreturn() function.
 
      
      
      
      
      
      
      Calling tpforward() can be used to indicate success up to that point in processing the request. If no application errors have been detected, you can invoke 
tpforward(), otherwise, you can call 
tpreturn() with 
rval set to 
TPFAIL.
 
      Listing 5‑9 is borrowed from the 
OPEN_ACCT service routine which is part of the 
ACCT server. This example illustrates how the service sends its data buffer to the 
DEPOSIT service by calling 
tpforward(). The code shows how to test the 
SQLCODE to determine whether the account insertion is successful. If the new account is added successfully, the branch record is updated to reflect the new account, and the data buffer is forwarded to the 
DEPOSIT service. On failure, 
tpreturn() is called with 
rval set to 
TPFAIL and the failure is reported on the status line of the form.
 
      
       ...
/* set pointer to TPSVCINFO data buffer */ 
transf = (FBFR *)transb->data;
...
/* Insert new account record into ACCOUNT*/ 
account_id = ++last_acct;    /* get new account number */
tlr_bal = 0.0;               /* temporary balance of 0 */ 
EXEC SQL insert into ACCOUNT (ACCOUNT_ID, BRANCH_ID, BALANCE,
ACCT_TYPE, LAST_NAME, FIRST_NAME, MID_INIT, ADDRESS, PHONE) values
(:account_id, :branch_id, :tlr_bal, :acct_type, :last_name,
      :first_name, :mid_init, :address, :phone);
if (SQLCODE != SQL_OK) {    /* Failure to insert */
      (void)Fchg(transf, STATLIN, 0,
          "Cannot update ACCOUNT", (FLDLEN)0);
      tpreturn(TPFAIL, 0, transb->data, 0L, 0);
}
 
/* Update branch record with new LAST_ACCT */
 
EXEC SQL update BRANCH set LAST_ACCT = :last_acct where BRANCH_ID = :branch_id;
if (SQLCODE != SQL_OK) {      /* Failure to update */
       (void)Fchg(transf, STATLIN, 0,
           "Cannot update BRANCH", (FLDLEN)0);
       tpreturn(TPFAIL, 0, transb->data, 0L, 0);
}
/* up the priority of the deposit call */
if (tpsprio(PRIORITY, 0L) == -1)
     (void)userlog("Unable to increase priority of deposit\n");
 
/* tpforward same buffer to deposit service to add initial balance */
tpforward("DEPOSIT", transb->data, 0L, 0);  
      
      
      
      
      
      
      An Oracle Tuxedo application administrator can use the advertise and 
unadvertise commands of 
tmadmin(1) to control the services offered by servers. The 
tpadvertise() and 
tpunadvertise() functions enable you to dynamically control the advertisement of a service in a request/response or conversational server. The service to be advertised (or unadvertised) must be available within the same server as the service making the request.
 
      
      
      
      Table 5‑4 describes the arguments to the 
tpadvertise() function.
 
      
      
      
      The tpunadvertise(3c) function removes the name of a service from the service table of the bulletin board so that the service is no longer advertised.
 
      
      tpunadvertise(char *svcname)
char *
svcname;
 
      The tpunadvertise() function contains one argument, which is described in 
Table 5‑5.
 
      
      
      
      Listing 5‑10 shows how to use the 
tpadvertise() function. In this example, a server called 
TLR is programmed to offer only the service called 
TLR_INIT when booted. After some initialization, 
TLR_INIT advertises two services called 
DEPOSIT and 
WITHDRAW. Both are performed by the 
tlr_funcs function, and both are built into the 
TLR server.
 
      
      
      extern void tlr_funcs()
 .
 .
 .
 if (tpadvertise("DEPOSIT", (tlr_funcs)(TPSVCINFO *)) == -1)
       check for errors;
 if (tpadvertise("WITHDRAW", (tlr_funcs)(TPSVCINFO *)) == -1)       
check for errors;
 if (tpunadvertise("TLR_INIT") == -1)       
check for errors;
 tpreturn(TPSUCCESS, 0, transb->data,0L, 0);
 
      
      
      
      
      
      buildserver  -o filename -f 
filenames -l 
filenames -s -v 
      Table 5‑6 describes the 
buildserver command-line options:
 
      
      
      
      
      
      By default, the buildserver command invokes the UNIX 
cc command. You can specify an alternative compile command and set your own flags for the compile and link-edit phases, however, by setting the 
CC and 
CFLAGS environment variables, respectively. For more information, refer to 
“Setting Environment Variables” on page 3‑5.
 
      The following command processes the acct.o application file and creates a server called 
ACCT that contains two services: 
NEW_ACCT, which calls the 
OPEN_ACCT function, and 
CLOSE_ACCT, which calls a function of the same name.
 
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      Because main() is provided by the Oracle Tuxedo system, you do not compile it directly. To ensure that the file is compiled using C++, you must use the C++ compiler with the 
buildserver command. By default, the 
buildserver command invokes the UNIX 
cc command. You can specify that the 
buildserver command invoke the C++ compiler, instead, by setting the 
CC environment variable to the full path name for the C++ compiler. Also, you can set flags for any options that you want to include on the C++ command line by setting the 
CFLAGS environment variable. For more information, refer to 
“Setting Environment Variables” on page 3‑5.