The Oracle Tuxedo system uses the tperrno(5) variable to supply information to a process when a function fails. All ATMI functions that normally return an integer or pointer return 
-1 or 
NULL, respectively, on error and set 
tperrno() to a value that describes the nature of the error. When a function does not return to its caller, as in the case of 
tpreturn() or 
tpforward(), which are used to terminate a service routine, the only way the system can communicate success or failure is through the variable 
tperrno() in the requester.
 
      The tperrordetail(3c) and 
tpstrerrordetail(3c) functions can be used to obtain additional detail about an error in the most recent Oracle Tuxedo system call on the current thread. 
tperrordetail() returns an integer (with an associated symbolic name) which is then used as an argument to 
tpstrerrordetail() to retrieve a pointer to a string that contains the error message. The pointer can then be used as an argument to 
userlog(3c) or 
fprintf(). For a list of the symbolic names that can be returned, refer to 
tperrordetail(3c) in the 
Oracle Tuxedo ATMI C Function Reference.
 
      tpurcode(5) is used to communicate user-defined conditions only. The system sets the value of 
tpurcode to the value of the 
rcode argument of 
tpreturn(). The system sets 
tpurcode, regardless of the value of the 
rval argument of 
tpreturn(), unless an error is encountered by 
tpreturn() or a transaction timeout occurs.
 
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      Oracle Tuxedo system errors indicate problems at the system level, rather than at the application level. When Oracle Tuxedo system errors occur, the system writes messages explaining the exact nature of the errors to the central event log, and returns 
TPESYSTEM in 
tperrno(5). For more information, refer to the 
“Central Event Log” on page 11‑41. Because these errors occur in the system, rather than in the application, you may need to consult the system administrator to correct them.
 
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      When tpsend() and 
tprecv() fail with a 
TPEEVENT error after a conversational connection is established, an event has occurred. Data may or may not be sent by 
tpsend(), depending on the event. The system returns 
TPEEVENT in the 
revent parameter passed to the function call and the course of action is dictated by the particular event.
 
      
      
      The TPEMATCH error code is returned in 
tperrno(5) when an attempt is made to perform an operation that results in a duplicate object. The following table lists the functions that may return the 
TPEMATCH error code and the associated cause.
 
      
      
        
          |  |  | 
        
          |  | The svcname specified is already advertised for the server but with a function other than func . Although the function fails, svcname  remains advertised with its current function (that is, func  does not replace the current function name). | 
        
          |  | The tranid points to a transaction identifier that another process has already resumed. In this case, the caller’s state with respect to the transaction is not changed. | 
        
          |  |  | 
      
      
      
      
      
      
      If tpreturn() encounters an error in processing or handling arguments, the system returns an error to the original requester and sets 
tperrno(5) to 
TPESVCERR. The receiver determines that an error has occurred by checking the value of 
tperrno(). The system does not send the data from the 
tpreturn() function, and if the failure occurred on 
tpgetrply(), it renders the call descriptor invalid. 
 
      If tpreturn() does not encounter the 
TPESVCERR error, then the value returned in 
rval determines the success or failure of the call. If the application specifies 
TPFAIL in the 
rval parameter, the system returns 
TPESVCFAIL in 
tperrno(5) and sends the data message to the caller. If 
rval is set to 
TPSUCCESS, the system returns successfully to the caller, 
tperrno() is not set, and the caller receives the data.
 
      
      The TPEBLOCK and 
TPGOTSIG error codes may be returned at the request or the reply end of a message and, as a result, can be returned for all communication calls. 
 
      The system returns TPEBLOCK when a blocking condition exists and the process sending a request (synchronously or asynchronously) indicates, by setting its 
flags parameter to 
TPPNOBLOCK, that it does not want to wait on a blocking condition. A blocking condition can exist when a request is being sent if, for example, all the system queues are full. 
 
      When tpcall() indicates a no blocking condition, only the sending part of the communication is affected. If a call successfully sends a request, the system does not return 
TPEBLOCK, regardless of any blocking situation that may exist while the call waits for the reply. 
 
      The system returns TPEBLOCK for 
tpgetrply() when a call is made with 
flags set to 
TPNOBLOCK and a blocking condition is encountered while 
tpgetrply() is awaiting the reply. This may occur, for example, if a message is not currently available.
 
      The TPGOTSIG error indicates an interruption of a system call by a signal; this situation is not actually an error condition. If the 
flags parameter for the communication functions is set to 
TPSIGRSTRT, the calls do not fail and the system does not return the 
TPGOTSIG error code in 
tperrno(5).
 
      
      
      
      
      The tpadmcall(3c) function returns 
TPEMIB in 
tperrno(5) in the event an administrative request fails. 
outbuf is updated and returned to the caller with FML32 fields indicating the cause of the error. For more information on the cause of the error, refer to 
MIB(5) and 
TM_MIB(5) in 
File Formats, Data Descriptions, MIBs, and System Processes Reference.
 
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      The tpenqueue(3c) or 
tpdequeue(3c) function returns 
TPEDIAGNOSTIC in 
tperrno(5) if the enqueuing or dequeuing on a specified queue fails. The reason for failure can be determined by the diagnostic returned via the 
ctl buffer. For a list of valid 
ctl flags, refer to 
tpenqueue(3c) or 
tpdequeue(3c) in the 
Oracle Tuxedo ATMI C Function Reference 
      
      
      For example, the TPERELEASE error may be returned if the 
TPACK flag is set when issuing the 
tpnotify(3c) function (indicating that the caller blocks until an acknowledgment message is received from the target client), but the target client is using an earlier release of the Oracle Tuxedo system that does not support the 
TPACK acknowledgement protocol. 
 
      
      Resource manager errors can occur with calls to tpopen(3c) and 
tpclose(3c), in which case the system returns the value of 
TPERMERR in 
tperrno(5). This error code is returned for 
tpopen() when the resource manager fails to open correctly. Similarly, this error code is returned for 
tpclose() when the resource manager fails to close correctly. To maintain portability, the Oracle Tuxedo system does not return a more detailed explanation of this type of failure. To determine the exact nature of a resource manager error, you must interrogate the resource manager.
 
      
      
      A blocking timeout specifies the maximum amount of time that an application waits for a reply to a service request. The application administrator defines the blocking timeout for the system in the configuration file. 
 
      A transaction timeout defines the duration of a transaction, which may involve several service requests. To define the transaction timeout for an application, pass the 
timeout argument to 
tpbegin().
 
      
      
      
      
      TPETIME indicates a blocking timeout on a communication call if the call was not made in transaction mode or if the 
flags parameter was not set to 
TPNOBLOCK.
 
      
        
          
            | 
                Note:	
               | If you set the TPNOBLOCK flag, a blocking timeout cannot occur because the call returns immediately if a blocking condition exists. | 
        
       
      
      
      
      
      
      
      The tpcall(), 
tpgetrply(), 
tpdequeue(3c), and 
tprecv() functions return 
TPEOTYPE when a reply message is sent in a buffer that is not recognized or not allowed by the caller. In the latter case, the buffer type is included in the type switch, but the type returned does not match the type that was allocated to receive the reply and a change in buffer type is not allowed by the caller. The caller indicates this preference by setting 
flags to 
TPNOCHANGE. In this case, strong type checking is enforced; the system returns 
TPEOTYPE when it is violated. By default, weak type checking is used. In this case, a buffer type other than the type originally allocated may be returned, as long as that type is recognized by the caller. The rules for sending replies are that the reply buffer must be recognized by the caller and, if strong type checking has been indicated, you must observe it.
 
      
      
      
      
      
      
      The ATMI supports three functions, tpstrerrordetail(3c), 
tpstrerror(3c), and 
Fstrerror, Fstrerror32(3fml), for retrieving the text of an error message from the message catalogs for the Oracle Tuxedo system and FML. The functions return pointers to the appropriate error messages. Your program can use a pointer to direct the referenced text to 
userlog(3c) or to another destination. For details, refer to 
tpstrerrordetail(3c) and 
tpstrerror(3c) in the 
Oracle Tuxedo ATMI C Function Reference, and 
Fstrerror, Fstrerror32(3fml) in the 
Oracle Tuxedo ATMI FML Function Reference.
 
      Listing 11‑1 shows a typical method of handling errors. The 
atmicall() function in this example represents a generic ATMI call. Note the code after the switch statement (line 21): it shows how 
tpurcode can be used to interpret an application-defined return code.
 
      
      001   #include <stdio.h>
002   #include "atmi.h"
003   
004   main()
005    
006   {
007   int rtnval;
008    
009   if (tpinit((TPINIT *) NULL) == -1)
010      error message, exit program;
011   if (tpbegin(30, 0) == -1)
012     
 error message, tpterm, exit program;
013    
014   
allocate any buffers,
015   
make atmi calls
016   
check return value
017    
018   rtnval = 
atmicall();
019    
020   if (rtnval == -1) {
021       switch(tperrno) {
022       case TPEINVAL:
023           fprintf(stderr, "Invalid arguments were given to 
atmicall\n");
024           fprintf(stderr, "e.g., service name was null or flags wrong\n");
025           break;
026       case ...:
027           fprintf(stderr, ". . .");
028           break;
029    
030   
Include all error cases described in the atmicall(3) reference 031   
page.
032   
Other return codes are not possible, so there should be no 033   
default within the switch statement.
034    
035   if (tpabort(0) == -1) {
036      char *p;
037      fprintf(stderr, "abort was attempted but failed\n");
038      p = tpstrerror(tperrno);
039      userlog("%s", p);
040   }
041   }
042   else
043   if (tpcommit(0) == -1)
044   fprintf(stderr, "REPORT program failed at commit time\n");
045    
046   
The following code fragment shows how an application-specific 
047   
return code can be examined.
048   .
049   .
050   .
051   ret = tpcall("servicename", (char*)sendbuf, 0, (char    **)&rcvbuf, &rcvlen, \
052   (long)0);
053   .
054   .
055   .
056   (void) fprintf(stdout, "Returned tpurcode is: %d\n", tpurcode);
057    
058    
059   free all buffers
060   tpterm();
061   exit(0);
062   }
 
      
      The values of tperrno(5) provide details about the nature of each problem and suggest the level at which it can be corrected. If your application defines a list of error conditions specific to your processing, the same can be said for the values of 
tpurcode.
 
      Listing 11‑2 shows how to use the 
tpstrerrordetail(3c) function to obtain additional detail when an error is encountered.
 
      
      001   #include <stdio.h>
002   #include <string.h>
003   #include <atmi.h>		/* BEA Tuxedo  Header File */
004   #define LOOP_ITER 	100
005   #if defined(__STDC__) || defined(__cplusplus)
006   main(int argc, char *argv[])
007   #else
008   main(argc, argv)
009   int argc;
010   char *argv[];
011   #endif
012   {
013   	char *sendbuf, *rcvbuf;
014   	long sendlen, rcvlen;
015   	int ret;
016   	int i;
017   	if(argc != 2) {
018   		(void) fprintf(stderr, "Usage: simpcl string\n");
019   		exit(1);
020   	}
021   	/* Attach to BEA Tuxedo System as a Client Process */
022   	if (tpinit((TPINIT *) NULL) == -1) {
023   		(void) fprintf(stderr, "Tpinit failed\n");
024   		exit(1);
025   	}
026   	sendlen = strlen(argv[1]);
027   
028   	/* Allocate STRING buffers for the request and the reply */
029   
030   	if((sendbuf = (char *) tpalloc("STRING", NULL, sendlen+1)) == NULL) {
031   		(void) fprintf(stderr,"Error allocating send buffer\n");
032   		tpterm();
033   		exit(1);
034   	}
035   
036   	if((rcvbuf = (char *) tpalloc("STRING", NULL, sendlen+1)) == NULL) {
037   		(void) fprintf(stderr,"Error allocating receive buffer\n");
038   		tpfree(sendbuf);
039   		tpterm();
040   		exit(1);
041   	}
042   
043   for( i=0; i<LOOP_ITER; i++) {
044   	(void) strcpy(sendbuf, argv[1]);
045   
046   	/* Request the service TOUPPER, waiting for a reply */
047   	ret = tpcall("TOUPPER", (char *)sendbuf, 0, (char **)&rcvbuf, &rcvlen, (long)0);
048   
049   	if(ret == -1) {
050   		(void) fprintf(stderr, "Can't send request to service
TOUPPER\n");
051   		(void) fprintf(stderr, "Tperrno = %d, %s\n", tperrno, tpstrerror(tperrno));
052   
053   		ret = tperrordetail(0); 
054   		if(ret == -1) {
055   			(void) fprintf(stderr, "tperrodetail() failed!\n");
056   			(void) fprintf(stderr, "Tperrno = %d, %s\n", tperrno, tpstrerror(tperrno));
057   		}
058   		else if (ret != 0) {
059   			 (void) fprintf( stderr, "errordetail:%s\n",
060   					tpstrerrordetail( ret, 0));
061   		}
062   		tpfree(sendbuf);
063   		tpfree(rcvbuf);
064   		tpterm();
065   		exit(1);
066   	}
067   	(void) fprintf(stdout, "Returned string is: %s\n", rcvbuf);
068   }
069   
070   	/* Free Buffers & Detach from System/T */
071   	tpfree(sendbuf);
072   	tpfree(rcvbuf);
073   	tpterm();
074   	return(0); 
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
        
          
            | • | Once a descriptor is used with tpgetrply() to retrieve a reply, or with tpsend()  or tprecv()  to report an error condition, it becomes invalid and any further reference to it returns TPEBADDESC . This rule is always observed, regardless of whether the code is running in transaction mode. | 
        
       
      
      
      
      
      
      
      
      
      
      
      
      
      
        
          
            | • | The type or subtype  of the reply buffer is not known or not allowed by the caller and, as a result, success or failure cannot be determined; tperrno(5)  is set to TPEOTYPE . | 
        
       
      
      
        
          
            | • | tpcommit() is called by a participant rather than by the originator of a transaction; tperrno(5)  is set to TPEPROTO . | 
        
       
      
      If tpcommit() is called after an initiator/participant failure or transaction timeout, the result is an implicit abort error. Then, because the commit failed, the transaction should be aborted.
 
      If the system returns TPESVCERR, 
TPESVCFAIL, 
TPEOTYPE, or 
TPETIME for any communication call, the transaction should be aborted explicitly with a call to 
tpabort(). You need not wait for outstanding call descriptors before explicitly aborting the transaction. However, because these descriptors are considered stale after the call is aborted, any attempt to access them after the transaction is terminated returns 
TPEBADDESC.
 
      In the case of TPESVCERR, 
TPESVCFAIL, and 
TPEOTYPE, communication calls continue to be allowed as long as the transaction has not timed out. When these errors are returned, the transaction is marked abort-only. To preserve the results of any further work, you should call any communication functions with the 
flags parameter set to 
TPNOTRAN. By setting this flag, you ensure that the work performed for the transaction marked “abort-only” will not be rolled back when the transaction is aborted.
 
      
      
      
      
      
      
      The tpcommit() function may return 
TPEHAZARD or 
TPEHEURISTIC, depending on how 
TP_COMMIT_CONTROL is set. 
 
      If you set TP_COMMIT_CONTROL to 
TP_CMT_LOGGED, the application obtains control before the second phase of a two-phase commit is performed. In this case, the application may not be aware of a heuristic decision that occurs during the second phase. 
 
      TPEHAZARD or 
TPEHEURISTIC can be returned in a one-phase commit, however, if a single resource manager is involved in the transaction and it returns a heuristic decision or a hazard indication during a one-phase commit.
 
      If you set TP_COMMIT_CONTROL to 
TP_CMT_COMPLETE, then the system returns 
TPEHEURISTIC if any resource manager reports a heuristic decision, and 
TPEHAZARD if any resource manager reports a hazard. 
TPEHAZARD specifies that a participant failed during the second phase of commit (or during a one-phase commit) and that it is not known whether a transaction completed successfully.
 
      
      
      
      
      
      
      When a process is in transaction mode and makes a communication call with flags set to 
TPNOTRAN, it prohibits the called service from becoming a participant in the current transaction. Whether the service request succeeds or fails has no impact on the outcome of the transaction. The transaction can still timeout while waiting for a reply that is due from a service, whether it is part of the transaction or not.
 
      
      
      
      Neither tpreturn() nor 
tpforward() should be called until all outstanding descriptors for the communication calls made within the service have been retrieved. If you call 
tpreturn() with outstanding descriptors for which 
rval is set to 
TPSUCCESS, the system encounters a protocol error and returns 
TPESVCERR to the process waiting on 
tpgetrply(). If the process is in transaction mode, the system marks the caller as “abort-only.” Even if the initiator of the transaction calls 
tpcommit(), the system implicitly aborts the transaction. If you call 
tpreturn() with outstanding descriptors for which 
rval is set to 
TPFAIL, the system returns 
TPESVCFAIL to the process waiting on 
tpgetrply(). The effect on the transaction is the same.
 
      When you call tpreturn() while running in transaction mode, this function can affect the result of the transaction by the processing errors that it encounters or that are retrieved from the value placed in 
rval by the application. 
 
      You can use tpforward() to indicate that success has been achieved up to a particular point in the processing of a request. If no application errors have been detected, the system invokes 
tpforward(); otherwise, the system invokes 
tpreturn() with 
TPFAIL. If you call 
tpforward() improperly, the system considers the call a processing error and returns a failed message to the requester.
 
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
        
          
            | • |  tpreturn() and tpforward() , when called by the participating service, place that service’s portion of the transaction in a state from which it can be either aborted or committed by the initiator. | 
        
       
      
      
      
        
          
            | • | The TPNOREPLY flag cannot be used when calling another service to participate in the current transaction. | 
        
       
      
      If you issue a communication call with the TPNOTRAN flag set and the called service is configured such that a transaction automatically starts when the service is called, the system places both the calling and called processes in transaction mode, but the two constitute different transactions. In this situation, the following facts apply:
 
      
        
          
            | • |  tpreturn() plays the initiator’s transaction role: it terminates the transaction in the service in which the transaction was automatically started. Alternatively, if the transaction is automatically started in a service that terminates with tpforward() , the tpreturn()  call issued in the last service in the forward chain plays the initiator’s transaction role: it terminates the transaction. (For an example, refer to the figure called “Transaction Roles of tpforward( ) and tpreturn() with AUTOTRAN” on page 11‑39 .) | 
        
       
      
        
          
            | • | Because it is in transaction mode, tpreturn() is vulnerable to the failure of any participant in the transaction, as well as to transaction timeouts. In this scenario, the system is more likely to return a failed message. | 
        
       
      
      
      
      
      
      
      
      
      
      
        
          
            | • | tpreturn() plays no transaction role; that is, the role of tpreturn()  is always the same, regardless of whether transactions are explicitly defined in the service routine. | 
        
       
      
        
          
            | • |  tpreturn() can return any value in rval , regardless of the outcome of the transaction. | 
        
       
      
      
      
      
      
      
      
      The Oracle Tuxedo system server calls tpsvrinit() or 
tpsvrthrinit(3c) during initialization. Specifically, 
tpsvrinit() or 
tpsvrthrinit(3c) is called after the calling process becomes a server but before it starts handling service requests. If 
tpsvrinit() or 
tpsvrthrinit(3c) performs any asynchronous communication, all replies must be retrieved before the function returns; otherwise, the system ignores all pending replies and the server exits. If 
tpsvrinit() or 
tpsvrthrinit(3c) defines any transactions, they must be completed with all asynchronous replies retrieved before the function returns; otherwise, the system aborts the transaction and ignores all outstanding replies. In this case, the server exits gracefully.
 
      The Oracle Tuxedo system server abstraction calls tpsvrdone() or 
tpsvrthrdone(3c) after it finishes processing service requests but before it exits. At this point, the server’s services are no longer advertised, but the server has not yet left the application. If 
tpsvrdone() or 
tpsvrthrdone(3c) initiates communication, it must retrieve all outstanding replies before it returns; otherwise, pending replies are ignored by the system and the server exits. If a transaction is started within 
tpsvrdone() or 
tpsvrthrdone(3c), it must be completed with all replies retrieved; otherwise, the system aborts the transaction and ignores the replies. In this case, too, the server exits.
 
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      The userlog(3c) signature is similar to that of the UNIX System 
printf(3S) function. The format portion of both functions can contain literal strings and/or conversion specifications for a variable number of arguments.
 
      
      Although you can use userlog(3c)statements to debug application software, it is sometimes necessary to use a debugger command for more complex problem solving.
 
      
      
      The standard UNIX system debugging command is dbx(1). For complete information about this tool, refer to 
dbx(1) in a UNIX system reference manual. If you use the 
-g option to compile client processes, you can debug those processes using the procedures described on the 
dbx(1) reference page. 
 
      
      
      
      
      
      
        
          
            | 3.	 | At the dbx prompt (* ), type the run  subcommand (r ) and any options you want to pass to the client program’s main() . | 
        
       
      
      The Oracle Tuxedo tmboot(1) command passes undocumented command-line options to the server’s predefined 
main(). To run a server directly, you must pass these options, manually, to the 
r subcommand. To find out which options need to be specified, run 
tmboot with the 
-n and 
-d 1 options. The 
-n option instructs 
tmboot not to execute a boot; 
-d 1 instructs it to display level 1 debugging statements. By default, the 
-d 1 option returns information about all processes. If you want information about only one process, you can specify your request accordingly with additional options. For more information, refer to the 
Oracle Tuxedo Command Reference. 
 
      The output of tmboot -n -d 1 includes a list of the command-line options passed by 
tmboot to the server’s 
main(), as shown in the following example: 
 
      
      
      
      You may not use dbx(1) to run a server that is already running as part of the configuration. If you try to do so, the server exits gracefully, indicating a duplicate server in the central event log.
 
      
      
      
      
      
      
      
      start msdev filename argument 
      
      
      
      
      
      
        
          
            | 2.	 | Within the HKEY_LOCAL_MACHINE subtree, navigate to \SOFTWARE\Microsoft\Windows\CurrentVersion\AeDebug | 
        
       
      
      
      
      
      
      
      
      
      
      The example shows how the set transaction statement (line 49) is used to set the consistency level and access mode of the transaction before the first SQL statement that accesses the database. (When read/write access is specified, the consistency level defaults to high consistency.) The SQL query determines the amount to be withdrawn in order to close the account based on the value of the 
ACCOUNT_ID (lines 50-58).
 
      tpalloc() allocates a buffer for the request message to the 
WITHDRAWAL service, and the 
ACCOUNT_ID and the amount to be withdrawn are placed in the buffer (lines 62-74). Next, a request is sent to the 
WITHDRAWAL service via a 
tpcall() call (line 79). An SQL 
delete statement then updates the database by removing the account in question (line 86).
 
      If all is successful, the buffer allocated in the service is freed (line 98) and the TPSVCINFO data buffer that was sent to the service is updated to indicate the successful completion of the transaction (line 99). Then, if the service was the initiator, the transaction is automatically committed. 
tpreturn() returns 
TPSUCCESS, along with the updated buffer, to the client process that requested the closing of the account. Finally, the successful completion of the requested service is reported on the status line of the form.
 
      
      
      
      001   #include <stdio.h>              /* UNIX */
002   #include <string.h>             /* UNIX */
003   #include <fml.h>                /* BEA Tuxedo System */
004   #include <atmi.h>               /* BEA Tuxedo System */
005   #include <Usysflds.h>           /* BEA Tuxedo System */
006   #include <sqlcode.h>            /* BEA Tuxedo System */
007   #include <userlog.h>            /* BEA Tuxedo System */
008   #include "bank.h"               /* BANKING #defines */
009   #include "bank.flds.h"          /* bankdb fields */
010   #include "event.flds.h"         /* event fields */
011
012
013   EXEC SQL begin declare section;
014   static long account_id;                    /* account id */
015   static long branch_id;                     /* branch id  */
016   static float bal, tlr_bal;                 /* BALANCE    */
017   static char acct_type;                     /* account type*/
018   static char last_name[20], first_name[20]; /* last name, first name */
019   static char mid_init;                      /* middle initial */
020   static char address[60];                   /* address    */
021   static char phone[14];                     /* telephone */
022   static long last_acct;                     /* last account branch gave */
023   EXEC SQL end declare section;
 
024   static FBFR *reqfb;             /* fielded buffer for request message */
025   static long reqlen;             /* length of request buffer */
026   static char amts[BALSTR];       /* string representation of float */
 
027   code for OPEN_ACCT service
 
028   /*
029    * Service to close an account
030    */
 
031   void
032   #ifdef __STDC__
033   LOSE_ACCT(TPSVCINFO *transb)
 
034   #else
 
035   CLOSE_ACCT(transb)
036   TPSVCINFO *transb;
037   #endif
 
038   {
039      FBFR *transf;               /* fielded buffer of decoded message */
 
040      /* set pointer to TPSVCINFO data buffer */
041         transf = (FBFR *)transb->data;
 
042      /* must have valid account number */
043      if (((account_id = Fvall(transf, ACCOUNT_ID, 0)) < MINACCT) ||
044       (account_id > MAXACCT)) {
045         (void)Fchg(transf, STATLIN, 0, "Invalid account number", (FLDLEN)0);
046         tpreturn(TPFAIL, 0, transb->data, 0L, 0);
047      }
 
048      /* Set transaction level */
049      EXEC SQL set transaction read write;
 
050      /* Retrieve AMOUNT to be deleted */
051      EXEC SQL declare ccur cursor for
052       select BALANCE from ACCOUNT where ACCOUNT_ID = :account_id;
053      EXEC SQL open ccur;
054      EXEC SQL fetch ccur into :bal;
055      if (SQLCODE != SQL_OK) {                /* nothing found */
056       (void)Fchg(transf, STATLIN, 0, getstr("account",SQLCODE), (FLDLEN)0);
057       EXEC SQL close ccur;
058       tpreturn(TPFAIL, 0, transb->data, 0L, 0);
059      }
 
060      /* Do final withdrawal */
 
061      /* make withdraw request buffer */
062      if ((reqfb = (FBFR *)tpalloc("FML",NULL,transb->len)) == (FBFR *)NULL) {
063       (void)userlog("tpalloc failed in close_acct\n");
064       (void)Fchg(transf, STATLIN, 0,
065           "Unable to allocate request buffer", (FLDLEN)0);
066       tpreturn(TPFAIL, 0, transb->data, 0L, 0);
067      }
068      reqlen = Fsizeof(reqfb);
069      (void)Finit(reqfb,reqlen);
 
070      /* put ID in request buffer */
071      (void)Fchg(reqfb,ACCOUNT_ID,0,(char *)&account_id, (FLDLEN)0);
 
072      /* put amount into request buffer */
073      (void)sprintf(amts,"%.2f",bal);
074      (void)Fchg(reqfb,SAMOUNT,0,amts, (FLDLEN)0);
 
075      /* increase the priority of this withdraw */
076      if (tpsprio(PRIORITY, 0L) == -1)
077       (void)userlog("Unable to increase priority of withdraw");
 
078      /* tpcall to withdraw service to remove remaining balance */
079      if (tpcall("WITHDRAWAL", (char *)reqfb, 0L, (char **)&reqfb,
080       (long *)&reqlen,TPSIGRSTRT) == -1) {
081       (void)Fchg(transf, STATLIN, 0,"Cannot make withdrawal", (FLDLEN)0);
082       tpfree((char *)reqfb);
083       tpreturn(TPFAIL, 0,transb->data, 0L, 0);
084      }
 
085      /* Delete account record */
 
086      EXEC SQL delete from ACCOUNT where current of ccur;
087      if (SQLCODE != SQL_OK) {                /* Failure to delete */
088       (void)Fchg(transf, STATLIN, 0,"Cannot close account", (FLDLEN)0);
089        EXEC SQL close ccur;
090        tpfree((char *)reqfb);
091        tpreturn(TPFAIL, 0, transb->data, 0L, 0);
092      }
093      EXEC SQL close ccur;
 
094      /* prepare buffer for successful return */
095      (void)Fchg(transf, SBALANCE, 0, Fvals(reqfb,SAMOUNT,0), (FLDLEN)0);
096      (void)Fchg(transf, FORMNAM, 0, "CCLOSE", (FLDLEN)0);
097      (void)Fchg(transf, STATLIN, 0, " ", (FLDLEN)0);
098      tpfree((char *)reqfb);
099      tpreturn(TPSUCCESS, 0, transb->data, 0L, 0);
100   }