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.
int
tpadvertise(char *svcname, void *
func)(TPSVCINFO *)
Table 5‑4 describes the arguments to the
tpadvertise() function.
Table 5‑5 describes the arguments to the
tpadvertisex() 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‑6.
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‑7 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.