Derek Atkins' RPC backend patch.

* src/engine/rpc/*: Gnucash RPC Subsystem (Backend and Server).
          Still considered experimental.  Note that the RPC protocol may
          change over time causing client/server incompatibilities between
          various versions of client and server.

        * configure.in: Add option to --enable-rpc.  Hook in RPC Subsystem

        * src/engine/Account.c: Fix for initial balance on
          stock/etc. accounts.  In particular, don't set to zero if there
          are no splits.

        * src/engine/Backend.h: New RPC errors

        * src/engine/Query.{c,h}: Add function to obtain the internal
          Query Predicate functions (for use in rebuilding Queries from
          the RPC subsystem)

        * src/engine/Transaction.c: Be sure to call the backend in
          xaccTransRollbackEdit()

        * src/engine/gnc-book.{c,h}: Added gnc_run_rpc_server() function.
                Added hooks to call RPC Backend

        * src/guile/gnc.gwp: added gnc_rpc_server() and new RPC Backend errors

        * src/scm/command-line.scm: added --rpc-server optio

        * src/engine/Makefile.am: Added RPC subdir


git-svn-id: svn+ssh://svn.gnucash.org/repo/gnucash/trunk@3801 57a11ea4-9604-0410-9ed3-97b8803252fd
This commit is contained in:
Dave Peticolas 2001-03-19 21:49:54 +00:00
parent afda83308b
commit 9f379e7fb7
41 changed files with 6242 additions and 55 deletions

View File

@ -1,3 +1,34 @@
2001-03-19 Derek Atkins <warlord@MIT.EDU>
* src/engine/rpc/*: Gnucash RPC Subsystem (Backend and Server).
Still considered experimental. Note that the RPC protocol may
change over time causing client/server incompatibilities between
various versions of client and server.
* configure.in: Add option to --enable-rpc. Hook in RPC Subsystem
* src/engine/Account.c: Fix for initial balance on
stock/etc. accounts. In particular, don't set to zero if there
are no splits.
* src/engine/Backend.h: New RPC errors
* src/engine/Query.{c,h}: Add function to obtain the internal
Query Predicate functions (for use in rebuilding Queries from
the RPC subsystem)
* src/engine/Transaction.c: Be sure to call the backend in
xaccTransRollbackEdit()
* src/engine/gnc-book.{c,h}: Added gnc_run_rpc_server() function.
Added hooks to call RPC Backend
* src/guile/gnc.gwp: added gnc_rpc_server() and new RPC Backend errors
* src/scm/command-line.scm: added --rpc-server optio
* src/engine/Makefile.am: Added RPC subdir
2001-03-19 Dave Peticolas <dave@krondo.com>
* src/gnome/gnc-dir.h.in: move to src/gnome. fix for make dist

View File

@ -172,6 +172,15 @@ AC_ARG_ENABLE( sql,
AC_SUBST(SQL_DIR)
### --------------------------------------------------------------------------
### RPC
AC_ARG_ENABLE( rpc,
[ --enable-rpc compile with rpc support],
RPC_DIR=rpc)
AC_SUBST(RPC_DIR)
### --------------------------------------------------------------------------
### i18n
AC_ARG_WITH( locale-dir,
@ -545,6 +554,7 @@ AC_OUTPUT(
src/doc/design/Makefile
src/engine/Makefile
src/engine/sql/Makefile
src/engine/rpc/Makefile
src/experimental/Makefile
src/experimental/cbb/Makefile
src/experimental/cbb/cbb-engine/Makefile

View File

@ -823,9 +823,9 @@ xaccAccountRecomputeBalance (Account * acc)
price_xfer(last_split, share_reconciled_balance);
}
else {
acc -> balance = gnc_numeric_zero();
acc -> cleared_balance = gnc_numeric_zero();
acc -> reconciled_balance = gnc_numeric_zero();
acc -> balance = dbalance;
acc -> cleared_balance = dcleared_balance;
acc -> reconciled_balance = dreconciled_balance;
}
} else {
acc -> share_balance = dbalance;

View File

@ -51,6 +51,19 @@ typedef enum {
ERR_SQL_CORRUPT_DB, /* data in db is corrupt */
ERR_SQL_MISSING_DATA, /* database doesn't contain expected data */
/* RPC errors */
ERR_RPC_BAD_URL, /* Can't parse url */
ERR_RPC_HOST_UNK, /* Host unknown */
ERR_RPC_CANT_CONNECT, /* bad hostname/port/dbname/etc. */
ERR_RPC_CANT_BIND, /* can't bind to address */
ERR_RPC_CANT_ACCEPT, /* can't accept connection */
ERR_RPC_NO_CONNECTION, /* no connection to server */
ERR_RPC_CONNECTION_LOST, /* Lost connection to server */
ERR_RPC_BAD_VERSION, /* RPC Version Mismatch */
ERR_RPC_SERVER_STATE, /* Invalid/bad server state */
ERR_RPC_FAILED, /* Operation failed */
ERR_RPC_NOT_ADDED, /* object not added */
} GNCBackendError;
/* NOTE: if you modify GNCBackendError, please update src/scm/gnc.gwp */

View File

@ -1,10 +1,10 @@
# Engine Makefile.am file.
# don't build sql subdir by default, unless you want hurt
SUBDIRS = ${SQL_DIR}
# don't build sql or RPC subdirs by default, unless you want hurt
SUBDIRS = ${SQL_DIR} ${RPC_DIR}
# but put it in the dist
DIST_SUBDIRS = sql
# but put them in the dist
DIST_SUBDIRS = sql rpc
# Build a libtool library, libhello.la for installation in libdir.
lib_LTLIBRARIES = libgncengine.la

View File

@ -1201,6 +1201,57 @@ xaccQueryGetTransactions (Query * q, query_run_t runtype) {
}
Predicate
xaccQueryGetPredicate (pr_type_t term_type)
{
Predicate p = NULL;
/* the predicates are only known in the local
* address space, which is why we have to set them
* from the abstract type here.
*/
switch (term_type)
{
case PR_ACCOUNT:
p = & xaccAccountMatchPredicate;
break;
case PR_ACTION:
p = & xaccActionMatchPredicate;
break;
case PR_AMOUNT:
p = & xaccAmountMatchPredicate;
break;
case PR_BALANCE:
p = & xaccBalanceMatchPredicate;
break;
case PR_CLEARED:
p = & xaccClearedMatchPredicate;
break;
case PR_DATE:
p = & xaccDateMatchPredicate;
break;
case PR_DESC:
p = & xaccDescriptionMatchPredicate;
break;
case PR_MEMO:
p = & xaccMemoMatchPredicate;
break;
case PR_NUM:
p = & xaccNumberMatchPredicate;
break;
case PR_PRICE:
p = & xaccSharePriceMatchPredicate;
break;
case PR_SHRS:
p = & xaccSharesMatchPredicate;
break;
case PR_MISC:
PERR ("misc term must not appear");
break;
}
return p;
}
/********************************************************************
* xaccQueryAddPredicate
* Add a predicate an existing query.
@ -1216,50 +1267,7 @@ xaccQueryAddPredicate (Query * q,
Query * qr;
qt->data = *pred;
/* the predicates are only known in the local
* address space, which is why we have to set them
* from the abstract type here.
*/
switch (pred->base.term_type)
{
case PR_ACCOUNT:
qt->p = & xaccAccountMatchPredicate;
break;
case PR_ACTION:
qt->p = & xaccActionMatchPredicate;
break;
case PR_AMOUNT:
qt->p = & xaccAmountMatchPredicate;
break;
case PR_BALANCE:
qt->p = & xaccBalanceMatchPredicate;
break;
case PR_CLEARED:
qt->p = & xaccClearedMatchPredicate;
break;
case PR_DATE:
qt->p = & xaccDateMatchPredicate;
break;
case PR_DESC:
qt->p = & xaccDescriptionMatchPredicate;
break;
case PR_MEMO:
qt->p = & xaccMemoMatchPredicate;
break;
case PR_NUM:
qt->p = & xaccNumberMatchPredicate;
break;
case PR_PRICE:
qt->p = & xaccSharePriceMatchPredicate;
break;
case PR_SHRS:
qt->p = & xaccSharesMatchPredicate;
break;
case PR_MISC:
PERR ("misc term must not appear");
break;
}
qt->p = xaccQueryGetPredicate (qt->data.base.term_type);
xaccInitQuery(qs, qt);
xaccQuerySetGroup(qs, q->acct_group);

View File

@ -306,4 +306,7 @@ time_t xaccQueryGetEarliestDateFound(Query * q);
time_t xaccQueryGetLatestDateFound(Query * q);
/* This is useful for network systems */
Predicate xaccQueryGetPredicate (pr_type_t term_type);
#endif

View File

@ -1476,6 +1476,7 @@ xaccTransCommitEdit (Transaction *trans)
void
xaccTransRollbackEdit (Transaction *trans)
{
Backend *be;
Transaction *orig;
int force_it=0, mismatch=0;
int i;
@ -1639,6 +1640,17 @@ xaccTransRollbackEdit (Transaction *trans)
}
}
be = xaccTransactionGetBackend (trans);
if (be && be->trans_rollback_edit)
{
int rc = 0;
rc = (be->trans_rollback_edit) (be, trans);
if (rc) {
PERR ("Rollback Failed. Ouch!");
}
}
xaccTransWriteLog (trans, 'R');
xaccFreeTransaction (trans->orig);

View File

@ -495,7 +495,8 @@ gnc_book_begin (GNCBook *book, const char * book_id,
/* -------------------------------------------------- */
if ((!strncmp(book_id, "http://", 7)) ||
(!strncmp(book_id, "https://", 8)) ||
(!strncmp(book_id, "postgres://", 11)))
(!strncmp(book_id, "postgres://", 11)) ||
(!strncmp(book_id, "rpc://", 6)))
{
char *p, *filefrag;
@ -570,6 +571,45 @@ gnc_book_begin (GNCBook *book, const char * book_id,
}
book->backend = (*pg_new) ();
} else
if (!strncmp(book_id, "rpc://", 6))
{
char * dll_err;
void * dll_handle;
Backend * (*rpc_new)(void);
/* open and resolve all symbols now (we don't want mystery
* failure later) */
dll_handle = dlopen ("libgnc_rpc.so", RTLD_NOW);
if (! dll_handle)
{
dll_err = dlerror();
PWARN (" can't load library: %s\n", dll_err);
g_free(book->fullpath);
book->fullpath = NULL;
g_free(book->book_id);
book->book_id = NULL;
gnc_book_push_error (book, ERR_BACKEND_NO_BACKEND);
return FALSE;
}
/* For the rpc backend, do the equivalent of
* the statically loaded
* book->backend = pgendNew (); */
rpc_new = dlsym (dll_handle, "rpcendNew");
dll_err = dlerror();
if (dll_err)
{
PWARN (" can't find symbol: %s\n", dll_err);
g_free(book->fullpath);
book->fullpath = NULL;
g_free(book->book_id);
book->book_id = NULL;
gnc_book_push_error (book, ERR_BACKEND_NO_BACKEND);
return FALSE;
}
book->backend = (*rpc_new) ();
}
/* if there's a begin method, call that. */
@ -651,7 +691,8 @@ gnc_book_load (GNCBook *book)
}
else if ((strncmp(book->book_id, "http://", 7) == 0) ||
(strncmp(book->book_id, "https://", 8) == 0) ||
(strncmp(book->book_id, "postgres://", 11) == 0))
(strncmp(book->book_id, "postgres://", 11) == 0) ||
(strncmp(book->book_id, "rpc://", 6)) == 0)
{
/* This code should be sufficient to initialize *any* backend,
* whether http, postgres, or anything else that might come along.
@ -1058,7 +1099,8 @@ xaccResolveURL (const char * pathfrag)
if (!strncmp (pathfrag, "http://", 7) ||
!strncmp (pathfrag, "https://", 8) ||
!strncmp (pathfrag, "postgres://", 11) )
!strncmp (pathfrag, "postgres://", 11) ||
!strncmp (pathfrag, "rpc://", 6))
{
return g_strdup(pathfrag);
}
@ -1069,3 +1111,36 @@ xaccResolveURL (const char * pathfrag)
return (xaccResolveFilePath (pathfrag));
}
void
gnc_run_rpc_server (void)
{
char * dll_err;
void * dll_handle;
int (*rpc_run)(short);
int ret;
/* open and resolve all symbols now (we don't want mystery
* failure later) */
dll_handle = dlopen ("libgnc_rpc.so", RTLD_NOW);
if (! dll_handle)
{
dll_err = dlerror();
PWARN (" can't load library: %s\n", dll_err);
return;
}
rpc_run = dlsym (dll_handle, "rpc_server_run");
dll_err = dlerror();
if (dll_err)
{
dll_err = dlerror();
PWARN (" can't find symbol: %s\n", dll_err);
return;
}
ret = (*rpc_run)(0);
/* XXX How do we force an exit? */
}

View File

@ -191,4 +191,7 @@ gboolean gnc_book_process_events (GNCBook *book);
char * xaccResolveFilePath (const char * filefrag);
char * xaccResolveURL (const char * pathfrag);
/* Run the RPC Server */
void gnc_run_rpc_server (void);
#endif /* __GNC_BOOK_H__ */

View File

@ -0,0 +1,4 @@
gncRpc.h
gncRpc_clnt.c
gncRpc_xdr.c
gncRpc_svc.c

View File

@ -0,0 +1,75 @@
# RPC library Makefile.am file.
# Created By: Derek Atkins <warlord@MIT.EDU>
# The GNC RPC Backend
lib_LTLIBRARIES = libgnc_rpc.la
libgnc_rpc_la_SOURCES = \
RpcBackend.c \
RpcServer.c \
RpcSock.c \
RpcUtils.c \
gncRpc_xdr.c \
gncRpc_clnt.c \
gncRpc_svc.c \
gncRpc_server.c \
clnt_thrd.c \
svc_thrd.c \
xprt_thrd.c
libgnc_rpc_la_LDFLAGS = -version-info 0:0:0
noinst_HEADERS = \
RpcBackend.h \
RpcServer.h \
RpcServerP.h \
RpcSock.h \
RpcUtils.h \
gncRpc.h \
clnt_thrd.h \
svc_thrd.h \
xprt_thrd.h
RPCGEN_FILES = \
gncRpc.h \
gncRpc_xdr.c \
gncRpc_clnt.c \
gncRpc_svc.c
EXTRA_DIST = \
.cvsignore \
README \
$(RPCGEN_SRCS)
INCLUDES = -I.. -I/usr/lib/glib/include
LDFLAGS = -lpthread
RPCGEN=rpcgen -M
RPCGEN_SRCS = gncAccount.x gncCommodity.x gncKVP.x gncGUID.x \
gncPrice.x gncQuery.x gncRpc.x gncSplit.x gncTxn.x
gncRpc.h: $(RPCGEN_SRCS)
$(RM) $@
$(RPCGEN) -h -o $@ gncRpc.x
gncRpc_xdr.c: $(RPCGEN_SRCS)
$(RM) $@
$(RPCGEN) -c -o $@ gncRpc.x
gncRpc_clnt.c: $(RPCGEN_SRCS)
$(RM) $@
$(RPCGEN) -l -o $@ gncRpc.x
gncRpc_svc.c: $(RPCGEN_SRCS)
$(RM) $@
$(RPCGEN) -m -o $@ gncRpc.x
#gncRpc_server_stubs.c: $(RPCGEN_SRCS)
# $(RPCGEN) -Ss -o $@ gncRpc.x
rpcgen: $(RPCGEN_FILES)
# Make sure the system knows to run rpcgen first
$(libgnc_rpc_la_SOURCES): gncRpc.h

67
src/engine/rpc/README Normal file
View File

@ -0,0 +1,67 @@
This directory contains code for RPC support. The RPC Backend is a
part of the client and connects to an RPC Server sitting elsewhere.
The RPC support in Gnucash is considered experimental, and you should
use it only at your own risk. In addition, the RPC Client and Server
must match, meaning that they must be using the same version of the
RPC protocol. The client does attempt to check the version of the
server and will complain if it does not match.
Version 1, 2001-03-17
GnuCash Build Instructions
--------------------------
Build the same as usual, but you must specify the flag '--enable-rpc'
to the configure (or autogen.sh), and then run 'make' as usual.
How to use GnuCash RPC
----------------------
GnuCash RPC has two parts, an RPC Server and an RPC Client. First,
choose your server machine and run the GnuCash RPC Server:
gnucash --rpc-server
Next, start the RPC Client on your workstation. You activate the RPC
Client by supplying a URL of the form rpc://hostname[:port]/<URL> to
your client. This will connect to the RPC Server on the specified
host and open the Gnucash file <URL> on the server. The <URL> must be
a complete Gnucash URL.
Example RPC URLs include:
rpc://localhost//path/to/gnucash-file.xac
rpc://serverhost:12345/file:/path/to/file.xac
rpc://host/postgres:/...
Things to do:
-------------
- Implement Security (encryption) on the RPC connection
- Better version checking in the RPC Protocol (allow client/server to
try to find a 'matching' protocol version?) This might require
more integration of the version number into RpcUtils
- username/password or other authentication methods
- better server integration with e.g. postgres (allow RPC Server to
authenticate to postgres on behalf of client, instead of in-band
authentication in the postgres URL)
- better error messages
- Support for pricedb
- cope with new accounts/transactions in begin_edit()
- grab the book_error more often for the result/error returns in the server
- better handling when the server connection is lost
provide a way to reconnect?
- better way to kill the server (currently can only 'kill' it or ^C it)
Derek Atkins <warlord@MIT.EDU>

919
src/engine/rpc/RpcBackend.c Normal file
View File

@ -0,0 +1,919 @@
/*
* FILE:
* RpcBackend.c
*
* FUNCTION:
* Implements the callbacks for the RPC (client) backend.
*
* HISTORY:
* Created By: Derek Atkins <warlord@MIT.EDU>
* Copyright (c) 2001, Derek Atkins
*/
#define _GNU_SOURCE
#include "config.h"
#include <rpc/xprt_thrd.h>
#include <glib.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <assert.h>
#include <pthread.h>
#include "AccountP.h"
#include "Backend.h"
#include "Group.h"
#include "GroupP.h"
#include "gnc-book.h"
#include "gnc-commodity.h"
#include "gnc-engine.h"
#include "gnc-engine-util.h"
#include "gnc-event.h"
#include "guid.h"
#include "TransactionP.h"
#include "gncRpc.h"
#include "RpcBackend.h"
#include "RpcSock.h"
#include "RpcUtils.h"
#define RPCEND_MAGIC 0x49c3a61c
#define VERIFY_BE(be,ret) { \
if ((be) == NULL) \
return (ret); \
assert (be->magic == RPCEND_MAGIC); \
}
#define VERIFY_BEV(be) { \
if ((be) == NULL) \
return; \
assert (be->magic == RPCEND_MAGIC); \
}
struct _rpcend {
Backend be; /* Backend */
gint32 magic; /* Magic number */
char * hostname; /* RPC Server */
char * portNum; /* Server Port Number */
char * dbName; /* Foreign Database Name */
GNCBook * book; /* My GNC Book, for insertions */
TXPRT * xprt; /* RPC Transport */
CLIENT * client; /* RPC Client */
RPCSock * sock; /* Socket */
pthread_mutex_t eventlock; /* Mutex around events members */
gboolean events; /* Are there events? */
/* For saving the hooks */
int nest_count; /* counter for nested disables */
Backend snr;
};
static void rpcendInit (RPCBackend *);
static short module = MOD_BACKEND;
/*******************************************************************/
/* Internal helper functions */
static void rpcendEnable (RPCBackend *be)
{
if (be->nest_count <= 0)
PERR ("too many nested disables");
be->nest_count --;
PINFO("nest count=%d", be->nest_count);
if (be->nest_count) return;
/* restore hooks */
be->be.account_begin_edit = be->snr.account_begin_edit;
be->be.account_commit_edit = be->snr.account_commit_edit;
be->be.trans_begin_edit = be->snr.trans_begin_edit;
be->be.trans_commit_edit = be->snr.trans_commit_edit;
be->be.trans_rollback_edit = be->snr.trans_rollback_edit;
be->be.run_query = be->snr.run_query;
be->be.sync = be->snr.sync;
}
static void rpcendDisable (RPCBackend *be)
{
if (be->nest_count < 0)
PERR ("too many nested enables");
be->nest_count ++;
PINFO("nest count=%d", be->nest_count);
if (be->nest_count > 1) return;
/* save hooks */
be->snr.account_begin_edit = be->be.account_begin_edit;
be->snr.account_commit_edit = be->be.account_commit_edit;
be->snr.trans_begin_edit = be->be.trans_begin_edit;
be->snr.trans_commit_edit = be->be.trans_commit_edit;
be->snr.trans_rollback_edit = be->be.trans_rollback_edit;
be->snr.run_query = be->be.run_query;
be->snr.sync = be->be.sync;
/* And turn off future calls */
be->be.account_begin_edit = NULL;
be->be.account_commit_edit = NULL;
be->be.trans_begin_edit = NULL;
be->be.trans_commit_edit = NULL;
be->be.trans_rollback_edit = NULL;
be->be.run_query = NULL;
be->be.sync = NULL;
}
static void myClose (void *arg)
{
RPCBackend *be = (RPCBackend *)arg;
if (be->client)
CLNT_DESTROY(be->client);
be->client = NULL;
if (be->xprt)
TXPRT_DESTROY (be->xprt);
be->xprt = NULL;
be->sock = NULL;
/* Turn off all the callbacks (should we do this manually?) */
rpcendDisable (be);
/* Should we set an event, here? */
/* Don't worry about setting the error here -- it will get set if
* the backend is called again
*/
}
static void rpcendLogin (RPCBackend *be)
{
unsigned short portNum = 0;
RPCSock *sock = NULL;
TXPRT *xprt = NULL;
CLIENT *client = NULL;
int err;
ENTER ("be=%p, host=\"%s\", port=\"%s\"", be, be->hostname, be->portNum);
/* Figure out the port number */
if (be->portNum)
portNum = (unsigned short) atoi (be->portNum);
if (!portNum)
portNum = RPCEND_PORT;
/* Convert portnum to net byte order */
portNum = htons (portNum);
/* Connect to the RPC Server */
if ((err = RpcConnect (be->hostname, portNum, &sock)) != 0) {
xaccBackendSetError (&be->be, err);
LEAVE ("connect");
return;
}
/* Build the RPC Transport */
if ((err = RpcTransport (sock, myClose, (void *)be, &xprt)) != 0) {
RpcClose (sock);
xaccBackendSetError (&be->be, err);
LEAVE ("transport");
return;
}
/* Get the RPC Client */
client = TXPRT_NEW_CLIENT (xprt, GNCRPC_PROG, GNCRPC_VERS);
if (client == NULL) {
RpcClose (sock);
xaccBackendSetError (&be->be, ERR_BACKEND_ALLOC);
LEAVE ("client");
return;
}
/* Verify the RPC Server Version */
err = 0;
gncrpc_version_1 (NULL, &err, client);
if (err != GNCRPC_PROTOCOL_VERSION) {
CLNT_DESTROY (client);
TXPRT_DESTROY (xprt);
RpcClose (sock);
xaccBackendSetError (&be->be, ERR_RPC_BAD_VERSION);
LEAVE ("rpc version");
return;
}
/* "Login" to the server */
/* Save the rpc information */
be->xprt = xprt;
be->sock = sock;
be->client = client;
LEAVE ("ok");
return;
}
static void rpcend_add_gncaccount (RPCBackend *be, gncAccount *acct,
AccountGroup *topgrp)
{
gnc_commodity_table *ct = gnc_book_get_commodity_table (be->book);
rpcend_do_add_acct (topgrp, acct, ct);
return;
}
static void rpcend_add_gncacctlist (RPCBackend *be,
AccountGroup *topgrp,
gnc_acctlist *acctlist)
{
for (; acctlist != NULL; acctlist = acctlist->next) {
rpcend_add_gncaccount (be, acctlist->acct, topgrp);
}
}
/* Return an int depicting whether this txn would be added to the engine:
* -1 == transaction is new(er) and would be added
* 0 == transaction version numbers match
* 1 == transaction is older than current engine cache
*/
static int rpcend_would_add_txn (gnc_vers_list *txn)
{
Transaction *trans;
GUID *guid;
int version;
if (!txn)
return 0;
guid = (GUID *)(txn->guid);
trans = xaccTransLookup (guid);
if (!trans)
return -1;
version = xaccTransGetVersion (trans);
if (version == txn->vers)
return 0;
else if (version > txn->vers)
return 1;
else
return -1;
}
/* Return an int depicting whether this txn was added to the engine:
* -1 == transaction is new(er) and was added
* 0 == transaction version numbers match; no changes
* 1 == transaction is older than current engine cache; no changes
*/
static int rpcend_add_transaction (RPCBackend *be, gncTransaction *txn)
{
int cache_is_newer;
gnc_commodity_table *ct;
if (!be || !txn) return 0;
/* disable callbacks and GUI events */
gnc_engine_suspend_events ();
rpcendDisable (be);
ct = gnc_book_get_commodity_table (be->book);
cache_is_newer = rpcend_do_add_txn (txn, ct);
/* re-enable events to the backend and GUI */
rpcendEnable (be);
gnc_engine_resume_events ();
return cache_is_newer;
}
static int rpcend_add_gnctransaction_list (RPCBackend *be, gnc_txnlist *tl)
{
for (; tl != NULL; tl = tl->next) {
rpcend_add_transaction (be, tl->txn);
}
return 0;
}
static void rpcend_add_gnccommoditylist (RPCBackend *be, gnc_commoditylist *cl)
{
gnc_commodity_table *ct = gnc_book_get_commodity_table (be->book);
rpcend_load_commoditylist (ct, cl);
}
/***************************************************************************/
/* Below this line are the 'exported interface' functions */
/*
* book_load will only load the commodity table and account tree
*/
static AccountGroup * rpcend_book_load (Backend *bend)
{
RPCBackend *be = (RPCBackend *)bend;
AccountGroup *ag = NULL;
gncrpc_ptr backend;
gncrpc_book_load_ret ret;
VERIFY_BE (be, NULL);
ENTER ("be=%p", be);
/* XXX: Handle the case where the connection was closed! */
memset (backend, 0, sizeof(backend));
memset (&ret, 0, sizeof (ret));
memcpy (backend, (char *)&be, sizeof(be));
gncrpc_book_load_1 (backend, &ret, be->client);
if (ret.error != 0) {
xaccBackendSetError (&be->be, ret.error);
return NULL;
}
/* suspend events */
gnc_engine_suspend_events ();
rpcendDisable (be);
/* Build the Commodity Table */
rpcend_add_gnccommoditylist (be, ret.commodities);
/* Parse the AccountGroup */
ag = gnc_book_get_group (be->book);
if (!ag)
ag = xaccMallocAccountGroup ();
rpcend_add_gncacctlist (be, ag, ret.acctlist);
/* Mark the newly read group as saved, since the act of putting
* it together will have caused it to be marked up as not-saved.
*/
xaccGroupMarkSaved (ag);
rpcendEnable (be);
gnc_engine_resume_events ();
/* Free the RPC results */
CLNT_FREERES (be->client, (xdrproc_t)xdr_gncrpc_book_load_ret, (caddr_t)&ret);
LEAVE ("be=%p, ag=%p", be, ag);
return ag;
}
static void rpcend_book_end (Backend *bend)
{
RPCBackend *be = (RPCBackend *)bend;
gncrpc_ptr backend;
int res = 0;
VERIFY_BEV (be);
ENTER ("be=%p", be);
/* XXX: Handle the case where the connection was closed */
if (be->client) {
memset (backend, 0, sizeof(backend));
memcpy (backend, (char *)&be, sizeof(be));
gncrpc_book_end_1 (backend, &res, be->client);
if (res != 0)
xaccBackendSetError (&be->be, res);
}
rpcendDisable (be);
/* Disconnect and cleanup XXX */
/* Now close the socket */
if (be->sock)
RpcClose (be->sock);
LEAVE ("be=%p", be);
}
static int rpcend_account_begin_edit (Backend *bend, Account *acct)
{
RPCBackend *be = (RPCBackend *)bend;
gncrpc_backend_guid args;
int ret = 0;
VERIFY_BE (be, -1);
ENTER ("be=%p, acc=%p", be, acct);
memset (&args, 0, sizeof (args));
memcpy (args.backend, (char *)&be, sizeof(be));
memcpy (args.guid, acct->guid.data, sizeof (args.guid));
gncrpc_account_begin_edit_1 (&args, &ret, be->client);
LEAVE ("be=%p, acc=%p (%s)", be, acct, acct ? acct->accountName : "");
return ret;
}
static int rpcend_account_rollback_edit (Backend *bend, Account *acct)
{
RPCBackend *be = (RPCBackend *)bend;
gncrpc_backend_guid args;
int ret = 0;
VERIFY_BE (be, -1);
ENTER ("be=%p, acc=%p (%s)", be, acct, acct ? acct->accountName : "");
if (acct == NULL)
return ERR_BACKEND_MISC;
memset (&args, 0, sizeof (args));
memcpy (args.backend, (char *)&be, sizeof(be));
memcpy (args.guid, acct->guid.data, sizeof (args.guid));
gncrpc_account_rollback_edit_1 (&args, &ret, be->client);
LEAVE ("be=%p, acc=%p, ret=%d", be, acct, ret);
return ret;
}
static int rpcend_account_commit_edit (Backend *bend, Account *acct)
{
RPCBackend *be = (RPCBackend *)bend;
gncrpc_commit_acct_args args;
int ret = 0;
AccountGroup *parent;
VERIFY_BE (be, -1);
ENTER ("be=%p, acc=%p (%s)", be, acct, acct ? acct->accountName : "");
if (acct == NULL)
return ERR_BACKEND_MISC;
parent = xaccAccountGetParent(acct);
/* First, see if we need to do anything here */
if (acct->core_dirty == FALSE) {
if (parent) parent->saved = 1;
PINFO ("core_dirty == FALSE");
return (rpcend_account_rollback_edit (bend, acct));
} else {
acct->version++;
}
memset (&args, 0, sizeof (args));
memcpy (args.backend, (char *)&be, sizeof(be));
/* Copy the Account information */
rpcend_build_gncacct (&(args.acct), acct);
#ifdef GNCACCT_COMMODITY
args.commodity = (gncCommodity *) xaccAccountGetCommodity (acct);
#else
args.currency = (gncCommodity *) xaccAccountGetCurrency (acct);
args.security = (gncCommodity *) xaccAccountGetSecurity (acct);
#endif
gncrpc_account_commit_edit_1 (&args, &ret, be->client);
/* Free up allocated data (in particular the kvp_frame */
rpcend_free_gnckvp (args.acct.kvp_data);
/* Set the save flag in the parent, even if the RPC failed */
if (parent) parent->saved = 1;
if (ret != 0) {
/* Failed to write; revert our copy */
acct->version--;
}
LEAVE ("be=%p, acc=%p, ret=%d", be, acct, ret);
return ret;
}
static int rpcend_trans_begin_edit (Backend *bend, Transaction *txn)
{
RPCBackend *be = (RPCBackend *)bend;
gncrpc_backend_guid args;
int ret = 0;
VERIFY_BE (be, -1);
ENTER ("be=%p, txn=%p", be, txn);
memset (&args, 0, sizeof (args));
memcpy (args.backend, (char *)&be, sizeof(be));
memcpy (args.guid, txn->guid.data, sizeof (args.guid));
gncrpc_txn_begin_edit_1 (&args, &ret, be->client);
LEAVE ("be=%p, txn=%p", be, txn);
return ret;
}
static int rpcend_trans_commit_edit (Backend *bend, Transaction *new,
Transaction *orig)
{
RPCBackend *be = (RPCBackend *)bend;
gncrpc_commit_txn_args args;
int ret = 0;
VERIFY_BE (be, -1);
ENTER ("be=%p, new=%p, vers=%d", be, new, new->version);
new->version++;
memset (&args, 0, sizeof (args));
memcpy (args.backend, (char *)&be, sizeof(be));
rpcend_build_gnctxn (&args.new, new);
gncrpc_txn_commit_edit_1 (&args, &ret, be->client);
rpcend_free_gnctxn (&args.new, FALSE);
if (ret != 0) {
/* Failed to write; backoff our version number */
new->version--;
}
LEAVE ("be=%p, new=%p, ret=%d, txn_vers=%d", be, new, ret, new->version);
return ret;
}
static int rpcend_trans_rollback_edit (Backend *bend, Transaction *txn)
{
RPCBackend *be = (RPCBackend *)bend;
gncrpc_backend_guid args;
int ret = 0;
VERIFY_BE (be, -1);
ENTER ("be=%p, txn=%p", be, txn);
memset (&args, 0, sizeof (args));
memcpy (args.backend, (char *)&be, sizeof(be));
memcpy (args.guid, txn->guid.data, sizeof (args.guid));
gncrpc_txn_rollback_edit_1 (&args, &ret, be->client);
LEAVE ("be=%p, txn=%p, ret=%d", be, txn, ret);
return ret;
}
static void rpcend_run_query (Backend *bend, Query *q)
{
RPCBackend *be = (RPCBackend *)bend;
gncrpc_query_args args;
gncrpc_query_ret ret;
gncrpc_get_txns_args txns_args;
gncrpc_get_txns_ret txns_ret;
VERIFY_BEV (be);
if (!q) return;
ENTER ("be=%p, q=%p", be, q);
memset (&args, 0, sizeof (args));
memset (&ret, 0, sizeof (ret));
memcpy (args.backend, (char *)&be, sizeof(be));
/* xaccQueryPrint (q);*/
/* Save off some non-transmitted information from the Query, and
* restore it after we run the query
*/
{
AccountGroup *actg;
gncQuery gq;
rpcend_build_gncquery (&gq, q);
args.query = &gq;
actg = (AccountGroup *)gq.acct_group;
gq.acct_group = NULL;
gq.split_list = NULL;
gq.xtn_list = NULL;
if (actg && actg->parent)
args.group_parent_guid = (gncGUID *) &(actg->parent->guid);
PINFO ("Calling 'run_query'");
gncrpc_run_query_1 (&args, &ret, be->client);
rpcend_free_gncquery (&gq);
}
if (ret.error != 0) {
xaccBackendSetError (&be->be, ret.error);
LEAVE ("be=%p, q=%p, query_1", be, q);
return;
}
/* Foreach transaction, figure out if it's in the engine or not. If
* it's not in the engine, or if the version implies an updated
* version, then add it to the list of txns to download. Then
* download all the 'updated' txns and add them to the engine
*/
{
gnc_vers_list *old = NULL, **endold = &old;
gnc_vers_list *new = NULL, **endnew = &new;
gnc_vers_list *verslist;
/* Figure out which transactions we need to add */
for (verslist = ret.txnlist; verslist != NULL; verslist = verslist->next) {
if (rpcend_would_add_txn (verslist) < 0) {
*endnew = verslist;
endnew = &(verslist->next);
} else {
*endold = verslist;
endold = &(verslist->next);
}
}
*endnew = NULL;
memset (&txns_ret, 0, sizeof (txns_ret));
if (new) {
/* Now grab the updated/new transactions */
memcpy (txns_args.backend, (char *)&be, sizeof(be));
txns_args.guids = new;
PINFO ("Calling 'get_txns'");
gncrpc_get_txns_1 (&txns_args, &txns_ret, be->client);
/* And now reconnect the new and old txnlist and put it back in
* the results so that freeres will destroy it all
*/
}
*endold = new;
ret.txnlist = old;
}
/* Free the results from the first RPC query */
CLNT_FREERES (be->client, (xdrproc_t)xdr_gncrpc_query_ret, (caddr_t)&ret);
/* Make sure this RPC was ok */
if (txns_ret.error != 0) {
xaccBackendSetError (&be->be, txns_ret.error);
LEAVE ("be=%p, q=%p, get_txns", be, q);
return;
}
/* Suspend events */
gnc_engine_suspend_events();
rpcendDisable(be);
/* Now, add all the transactions to the engine */
rpcend_add_gnctransaction_list (be, txns_ret.txnlist);
/* Resume events */
rpcendEnable(be);
gnc_engine_resume_events();
/* And free the results */
CLNT_FREERES (be->client, (xdrproc_t)xdr_gncrpc_get_txns_ret, (caddr_t)&txns_ret);
LEAVE ("be=%p, q=%p", be, q);
}
static void rpcend_sync (Backend *bend, AccountGroup *acctgrp)
{
RPCBackend *be = (RPCBackend *)bend;
gncrpc_sync1_args args1;
gncrpc_sync1_ret ret1;
gncrpc_sync2_args args2;
int ret2 = -1;
gnc_commodity_table *ct = gnc_book_get_commodity_table (be->book);
VERIFY_BEV (be);
ENTER ("be=%p, ag=%p", be, acctgrp);
memset (&args1, 0, sizeof (args1));
memset (&ret1, 0, sizeof (ret1));
memcpy (args1.backend, (char *)&be, sizeof(be));
args1.commodities = rpcend_build_gnccommoditylist (ct, FALSE);
args1.acctlist = rpcend_build_gncacct_verslist (acctgrp, FALSE);
args1.txnlist = rpcend_build_gnctxn_verslist (acctgrp, FALSE);
/* Call RPC */
gncrpc_sync1_1 (&args1, &ret1, be->client);
/* Free the arguments of request1 */
rpcend_free_gnccommoditylist (args1.commodities, FALSE);
rpcend_free_verslist (args1.txnlist, FALSE);
rpcend_free_verslist (args1.acctlist, FALSE);
if (ret1.error != 0) {
xaccBackendSetError (&be->be, ret1.error);
CLNT_FREERES (be->client, (xdrproc_t)xdr_gncrpc_sync1_ret, (caddr_t)&ret1);
LEAVE ("be=%p, ag=%p, sync1", be, acctgrp);
return;
}
/* XXX: Do we need to update any version numbers here? */
/*
* Now, add the accounts and transactions that the server sent us,
* and send the server the accounts and transactions that the server
* requested
*/
/* Suspend events */
gnc_engine_suspend_events();
rpcendDisable(be);
/* Add all the commoditities, accounts, and transactions to the engine */
rpcend_add_gnccommoditylist (be, ret1.commodities);
rpcend_add_gncacctlist (be, acctgrp, ret1.acctlist);
rpcend_add_gnctransaction_list (be, ret1.txnlist);
/* Then build the response to send back to the server */
memset (&args2, 0, sizeof (args2));
memcpy (args2.backend, (char *)&be, sizeof(be));
args2.acctlist = rpcend_build_gncacctlist_list (acctgrp,
ret1.send_acctlist);
args2.txnlist = rpcend_build_gnctxnlist_list (acctgrp,
ret1.send_txnlist);
/* Free the first results */
CLNT_FREERES (be->client, (xdrproc_t)xdr_gncrpc_sync1_ret, (caddr_t)&ret1);
/* set everything as saved */
xaccGroupMarkSaved (acctgrp);
/* Resume events */
rpcendEnable(be);
gnc_engine_resume_events();
/* Now send this back to the server */
gncrpc_sync2_1 (&args2, &ret2, be->client);
/* Free the arguments to request 2 */
rpcend_free_gnctxnlist (args2.txnlist);
rpcend_free_gncacctlist (args2.acctlist);
/* And return */
xaccBackendSetError (&be->be, ret2);
LEAVE ("be=%p, ag=%p", be, acctgrp);
return;
}
static gboolean rpcend_events_pending (Backend *bend)
{
RPCBackend *be = (RPCBackend *)bend;
VERIFY_BE (be, FALSE);
/* The events flag can only be cleared from within this thread of
* control. So, the worst thing that can happen here is that we
* have a false-negative.
*/
return be->events;
}
static gboolean rpcend_process_events (Backend *bend)
{
RPCBackend *be = (RPCBackend *)bend;
gboolean changed = FALSE;
VERIFY_BE (be, FALSE);
pthread_mutex_lock (&(be->eventlock));
if (!be->events) {
pthread_mutex_unlock (&(be->eventlock));
return FALSE;
}
/* Yep, there are events. Copy off the event information, then
* unlock the Backend event handler. Then go off and process the
* events. Note that one of the events could be "disconnected from
* server", so check for that first!
*/
/* Process any waiting events */
/* XXX */
return changed;
}
static void rpcend_book_begin (GNCBook *book, const char *book_id,
gboolean ignore_lock, gboolean create)
{
RPCBackend *be;
char *url, *start, *end, *rest;
if (!book) return;
be = (RPCBackend *) xaccGNCBookGetBackend (book);
VERIFY_BEV (be);
ENTER("be=%p, id=%s, ignore=%s, create=%s", be, book_id,
(ignore_lock == TRUE ? "true" : "false"),
(create == TRUE ? "true" : "false"));
/* close any dangling sessions from before and then reinitialize */
rpcend_book_end ((Backend *)be);
rpcendInit (be);
/* Remember my book */
be->book = book;
/* Parse the book_id for the hostname and db name.
* The expected URL format is:
* rpc://host[:port]/db_name
*/
if (strncmp (book_id, "rpc://", 6)) {
xaccBackendSetError (&be->be, ERR_RPC_BAD_URL);
LEAVE ("Not an RPC URL?");
return;
}
url = g_strdup(book_id);
start = url + 6;
rest = strchr (start, '/');
if (!rest || *rest == '\0') {
xaccBackendSetError (&be->be, ERR_RPC_BAD_URL);
g_free (url);
LEAVE ("cannot find a path after host[:port]");
return;
}
*rest = '\0';
end = strchr (start, ':');
if (end) {
/* end would have the port number */
*end = '\0';
be->portNum = g_strdup (end+1);
}
be->hostname = g_strdup (start);
start = rest+1;
if (*start == '\0') {
xaccBackendSetError (&be->be, ERR_RPC_BAD_URL);
g_free (url);
LEAVE ("tailing slash but no path after host[:port]");
return;
}
/* dbname is the last thing before any url-encoded data */
be->dbName = g_strdup (start);
/* I would parse url-encoded data here, but I don't have any */
/* XXX */
/* make the connection */
rpcendLogin (be);
/* Free the URL */
g_free (url);
if (be->xprt == NULL)
return;
/* Call the remote procedure to open the book */
{
gncrpc_book_begin_args args;
int res = 0;
memset (&args, 0, sizeof (args));
memcpy (args.book, (char *)&book, sizeof (book));
memcpy (args.backend, (char *)&be, sizeof (be));
args.book_id = be->dbName;
args.ignore_lock = ignore_lock;
args.create = create;
gncrpc_book_begin_1 (&args, &res, be->client);
if (res != 0) {
RpcClose (be->sock);
xaccBackendSetError (&be->be, res);
LEAVE ("begin");
return;
}
}
/* Setup callbacks */
rpcendEnable (be);
be->be.book_end = rpcend_book_end;
be->be.book_load = rpcend_book_load;
be->be.account_begin_edit = rpcend_account_begin_edit;
be->be.account_commit_edit = rpcend_account_commit_edit;
be->be.trans_begin_edit = rpcend_trans_begin_edit;
be->be.trans_commit_edit = rpcend_trans_commit_edit;
be->be.trans_rollback_edit = rpcend_trans_rollback_edit;
be->be.run_query = rpcend_run_query;
be->be.sync = rpcend_sync;
be->be.events_pending = rpcend_events_pending;
be->be.process_events = rpcend_process_events;
LEAVE("be=%p, id=%s", be, book_id);
}
static void
rpcendInit (RPCBackend *be)
{
memset (be, 0, sizeof (*be));
/* The only callback that should work is Begin */
be->be.book_begin = rpcend_book_begin;
rpcendDisable (be);
be->be.last_err = ERR_BACKEND_NO_ERR;
/* RPC specific data */
be->magic = RPCEND_MAGIC;
/* Initialize the event mutex */
pthread_mutex_init (&(be->eventlock), NULL);
}
Backend *
rpcendNew (void)
{
RPCBackend *be;
be = (RPCBackend *) g_malloc (sizeof (*be));
rpcendInit (be);
return (Backend *) be;
}

View File

@ -0,0 +1,25 @@
/*
* FILE:
* RpcBackend.h
*
* FUNCTION:
* Implements the callbacks for the RPC (client) backend.
*
* HISTORY:
* Created By: Derek Atkins <warlord@MIT.EDU>
* Copyright (c) 2001, Derek Atkins
*/
#ifndef __RPC_BACKEND_H
#define __RPC_BACKEND_H
#include "BackendP.h"
typedef struct _rpcend RPCBackend;
/*
* rpcendNew() creates a new RPC Backend
*/
Backend * rpcendNew (void);
#endif /* __RPC_BACKEND_H */

116
src/engine/rpc/RpcServer.c Normal file
View File

@ -0,0 +1,116 @@
/*
* FILE:
* RpcServer.c
*
* FUNCTION:
* Implements the Gnucash RPC server.
*
* HISTORY:
* Created By: Derek Atkins <warlord@MIT.EDU>
* Copyright (c) 2001, Derek Atkins
*/
#define _GNU_SOURCE
#include "config.h"
#include <rpc/xprt_thrd.h>
#include "gnc-engine-util.h"
#include "RpcServer.h"
#include "RpcServerP.h"
#include "RpcSock.h"
#include "gncRpc.h"
extern void gncrpc_prog_1(struct svc_req *, register SVCXPRT *);
static short module = MOD_BACKEND;
static void myClose (void *arg)
{
GncRpcSvc *cl = (GncRpcSvc *)arg;
if (!cl)
return;
TXPRT_DESTROY (cl->xprt);
if (cl->clnt)
CLNT_DESTROY (cl->clnt);
if (cl->book)
gnc_book_destroy (cl->book);
PINFO ("Client Disconnected: %p", cl);
/* Remove myself from the Client List */
cl->clist->clist = g_list_remove (cl->clist->clist, cl);
g_free (cl);
}
int rpc_server_run (unsigned short port)
{
RPCSock *master;
int ret;
int run = 1;
GncRPCClist clist;
ENTER ("port=%d", port);
if (!port)
port = RPCEND_PORT;
if ((ret = RpcCreateListener (htons(port), &master)) != 0) {
LEAVE ("listener failed");
return ret;
}
fprintf (stderr, "RPC Server Running...\n");
while (run) {
GncRpcSvc *new = g_malloc (sizeof (*new));
if (!new) {
LEAVE ("g_malloc failed");
return -1;
}
memset (new, 0, sizeof (*new));
new->book = gnc_book_new ();
if (!new->book) {
g_free (new);
LEAVE ("gnc_book_new() failed");
return -2;
}
new->clist = &clist;
/* Setup to listen */
RpcListen (master, 3);
/* Grab the next client */
if ((ret = RpcAccept (master, &(new->sock))) != 0) {
run = 0;
LEAVE ("Accept failed (%d)", ret);
break;
}
/* XXX Authenticate? */
/* Build the RPC Transport */
if ((ret = RpcTransport (new->sock, myClose, (void *)new, &(new->xprt)))
!= 0) {
RpcClose (new->sock);
run = 0;
LEAVE ("Transport failed (%d)", ret);
break;
}
/* Add this client to the clist */
clist.clist = g_list_prepend (clist.clist, new);
/* Register the service */
TXPRT_REG_CALLOUT (new->xprt, GNCRPC_PROG, GNCRPC_VERS, gncrpc_prog_1);
PINFO ("New Client connected: %p", new);
}
return ret;
}

View File

@ -0,0 +1,20 @@
/*
* FILE:
* RpcServer.h
*
* FUNCTION:
* Implements the Gnucash RPC server.
*
* HISTORY:
* Created By: Derek Atkins <warlord@MIT.EDU>
* Copyright (c) 2001, Derek Atkins
*/
#ifndef __RPC_SERVER_H
#define __RPC_SERVER_H
typedef struct _gncrpc_svc GncRpcSvc;
int rpc_server_run (unsigned short port);
#endif /* __RPC_SERVER_H */

View File

@ -0,0 +1,37 @@
/*
* FILE:
* RpcServerP.h
*
* FUNCTION:
* Gnucash RPC Server Private Header
*
* HISTORY:
* Created By: Derek Atkins <warlord@MIT.EDU>
* Copyright (c) 2001, Derek Atkins
*/
#ifndef __RPC_SERVERP_H
#define __RPC_SERVERP_H
#include <rpc/xprt_thrd.h>
#include "RpcServer.h"
#include "RpcSock.h"
#include "gncRpc.h"
#include "gnc-book.h"
typedef struct _gncrpc_clist {
GList *clist;
} GncRPCClist;
struct _gncrpc_svc {
TXPRT * xprt; /* Transport */
RPCSock * sock; /* Socket */
GNCBook * book; /* My Client's Book */
CLIENT * clnt; /* Client's Callback Object */
GncRPCClist * clist; /* Client List */
};
#endif /* __RPC_SERVERP_H */

252
src/engine/rpc/RpcSock.c Normal file
View File

@ -0,0 +1,252 @@
/*
* FILE:
* RpcSock.c
*
* FUNCTION:
* Implements the RPC Socket connection
*
* HISTORY:
* Created By: Derek Atkins <warlord@MIT.EDU>
* Copyright (c) 2001, Derek Atkins
*/
#include <netdb.h>
#include <unistd.h>
#include <rpc/xprt_thrd.h>
#include "RpcBackend.h"
#include "RpcSock.h"
struct _rpcend_sock {
int sock; /* socket */
TXPRT * xprt; /* Transport */
void * cb_arg; /* Callback Argument */
void (*close)(void *cb); /* Close callback */
gboolean listener; /* Is this a master listener? */
struct sockaddr_in peer; /* Peer Address */
};
static int
myread (caddr_t sockp, char *buf, int len)
{
RPCSock *sock = (RPCSock *)sockp;
if (len == 0)
return 0;
switch (len = read (sock->sock, buf, len))
{
case 0:
/* premature eof */
/* ECONNRESET, RPC_CANTRECV */
len = -1; /* it's really an error */
break;
case -1:
/* RPC_CANTRECV */
break;
}
return len;
}
static int
mywrite (caddr_t sockp, char *buf, int len)
{
RPCSock *sock = (RPCSock *)sockp;
int i, cnt;
for (cnt = len; cnt > 0; cnt -= i, buf += i)
{
if ((i = write (sock->sock, buf, cnt)) == -1)
{
/* RPC_CANTSEND */
return -1;
}
}
return len;
}
static int
myselect (caddr_t sockp)
{
RPCSock *sock = (RPCSock *)sockp;
struct timeval timeout;
fd_set fds;
timeout.tv_sec = 2;
timeout.tv_usec = 0;
FD_ZERO(&fds);
FD_SET(sock->sock, &fds);
return select (sock->sock+1, &fds, 0, 0, &timeout);
}
static void
myclose (caddr_t sockp)
{
RPCSock *sock = (RPCSock *)sockp;
/* Close callback */
if (sock->close)
(*sock->close)(sock->cb_arg);
close (sock->sock);
g_free (sock);
}
int RpcConnect (char *hostname, unsigned short port, RPCSock **sock)
{
RPCSock *new;
int s;
struct hostent *hp;
struct sockaddr_in sin;
if (hostname == NULL || *hostname == '\0' || port == 0)
return ERR_RPC_BAD_URL;
if (sock == NULL)
return ERR_BACKEND_MISC;
if ((hp = gethostbyname (hostname)) == NULL)
return ERR_RPC_HOST_UNK;
sin.sin_family = hp->h_addrtype;
sin.sin_port = port;
memcpy (&sin.sin_addr, hp->h_addr, MIN(sizeof(sin.sin_addr),hp->h_length));
if ((s = socket (sin.sin_family, SOCK_STREAM, 0)) < 0)
return ERR_BACKEND_ALLOC;
if (connect (s, &sin, sizeof(sin)) != 0)
return ERR_RPC_CANT_CONNECT;
new = g_malloc (sizeof (*new));
if (new == NULL) {
close (s);
return ERR_BACKEND_ALLOC;
}
memset (new, 0, sizeof (*new));
new->sock = s;
*sock = new;
return 0;
}
int RpcClose (RPCSock *sock)
{
if (sock == NULL)
return ERR_BACKEND_MISC;
myclose ((caddr_t) sock);
return 0;
}
int RpcTransport (RPCSock *sock, void (*myClose)(void *arg), void *arg,
TXPRT **xprt)
{
TXPRT *x;
if (sock == NULL || xprt == NULL || myClose == NULL || sock->xprt != NULL)
return ERR_BACKEND_MISC;
x = xprt_thrd_create ((caddr_t)sock, myread, mywrite, myclose, myselect,
0, 0);
if (x == NULL)
return ERR_BACKEND_ALLOC;
sock->close = myClose;
sock->cb_arg = arg;
sock->xprt = *xprt = x;
return 0;
}
int RpcCreateListener (unsigned short port, RPCSock **sock)
{
int s;
int on = 1;
struct sockaddr_in sin;
RPCSock *new;
if (!sock)
return ERR_BACKEND_MISC;
/* Create socket */
if ((s = socket (AF_INET, SOCK_STREAM, 0)) < 0)
return ERR_BACKEND_ALLOC;
/* Set socket options */
if (setsockopt (s, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on))
< 0) {
close (s);
return ERR_BACKEND_MISC;
}
/* Bind to the address */
sin.sin_family = AF_INET;
sin.sin_port = port;
sin.sin_addr.s_addr = INADDR_ANY;
if (bind (s, &sin, sizeof (sin)) < 0) {
close (s);
return ERR_RPC_CANT_BIND;
}
/* Build RPCSock */
new = g_malloc (sizeof (*new));
if (new == NULL) {
close (s);
return ERR_BACKEND_ALLOC;
}
memset (new, 0, sizeof (*new));
new->sock = s;
new->listener = TRUE;
*sock = new;
return 0;
}
int RpcListen (RPCSock *sock, int val)
{
if (!sock)
return -1;
if (!sock->listener)
return -2;
return (listen (sock->sock, val));
}
int RpcAccept (RPCSock *master, RPCSock **client)
{
int s;
socklen_t len;
RPCSock *new;
if (!master || !client)
return ERR_BACKEND_MISC;
if (!master->listener)
return ERR_BACKEND_MISC;
new = g_malloc (sizeof (*new));
if (!new)
return ERR_BACKEND_ALLOC;
memset (new, 0, sizeof (*new));
len = sizeof (new->peer);
if ((new->sock = accept (master->sock, &(new->peer), &len)) < 0) {
g_free (new);
return ERR_RPC_CANT_ACCEPT;
}
*client = new;
return 0;
}
void * RpcGetData (RPCSock *sock)
{
if (!sock)
return NULL;
return (sock->cb_arg);
}

34
src/engine/rpc/RpcSock.h Normal file
View File

@ -0,0 +1,34 @@
/*
* FILE:
* RpcSock.h
*
* FUNCTION:
* Implements the RPC Socket connection
*
* HISTORY:
* Created By: Derek Atkins <warlord@MIT.EDU>
* Copyright (c) 2001, Derek Atkins
*/
#ifndef __RPC_SOCK_H
#define __RPC_SOCK_H
#include <rpc/xprt_thrd.h>
#include "RpcBackend.h"
#define RPCEND_PORT 11207
typedef struct _rpcend_sock RPCSock;
int RpcConnect (char *hostname, unsigned short port, RPCSock **sock);
int RpcClose (RPCSock *sock);
int RpcCreateListener (unsigned short port, RPCSock **sock);
int RpcListen (RPCSock *sock, int val);
int RpcAccept (RPCSock *master, RPCSock **client);
int RpcTransport (RPCSock *sock, void (*myClose)(void *arg), void *cb_arg,
TXPRT **xprt);
void * RpcGetData (RPCSock *sock);
#endif /* __RPC_SOCK_H */

1270
src/engine/rpc/RpcUtils.c Normal file

File diff suppressed because it is too large Load Diff

71
src/engine/rpc/RpcUtils.h Normal file
View File

@ -0,0 +1,71 @@
/*
* FILE:
* RpcUtils.h
*
* FUNCTION:
* Implements some utility functions for the RPC (client) backend.
*
* HISTORY:
* Created By: Derek Atkins <warlord@MIT.EDU>
* Copyright (c) 2001, Derek Atkins
*/
#ifndef __RPC_UTILS_H
#define __RPC_UTILS_H
#include "gncRpc.h"
#include "Account.h"
#include "RpcBackend.h"
/*
* This number should be increased every time the RPC protocol
* changes. This means every time any of the .x file change, this
* number should be increased.
*/
#define GNCRPC_PROTOCOL_VERSION 1
gnc_kvp_frame *rpcend_build_gnckvp (kvp_frame *frame);
void rpcend_free_gnckvp (gnc_kvp_frame *frame);
kvp_frame * rpcend_parse_gnckvp (gnc_kvp_frame *data);
void rpcend_build_gnctxn (gncTransaction *gnctxn, Transaction *txn);
void rpcend_free_gnctxn (gncTransaction *gnctxn, gboolean freetxn);
void rpcend_build_gncacct (gncAccount *gncacct, Account *acc);
void rpcend_free_gncacct (gncAccount *acc, gboolean freeacct);
gnc_txnlist * rpcend_build_gnctxnlist_list (AccountGroup *ag,
gnc_vers_list *txnlist);
void rpcend_free_gnctxnlist (gnc_txnlist *txnlist);
gnc_acctlist * rpcend_build_gncacctlist_list (AccountGroup *ag,
gnc_vers_list *acctlist);
gnc_acctlist * rpcend_build_gncacctlist (AccountGroup *ag);
void rpcend_free_gncacctlist (gnc_acctlist *acctlist);
gnc_vers_list * rpcend_build_gncacct_verslist (AccountGroup *ag,
gboolean copyguid);
gnc_vers_list * rpcend_build_gnctxn_verslist (AccountGroup *ag,
gboolean copyguid);
gnc_vers_list * rpcend_build_gncverslist_txn (GList *txnlist,
gboolean copyguid);
void rpcend_free_verslist (gnc_vers_list *vlist, gboolean freeguid);
gnc_commoditylist * rpcend_build_gnccommoditylist (gnc_commodity_table *ct,
gboolean copycom);
void rpcend_free_gnccommoditylist (gnc_commoditylist *clist, gboolean freecom);
void rpcend_load_gnccommodity (gnc_commodity_table *ct, gncCommodity *com);
void rpcend_load_commoditylist (gnc_commodity_table *ct,
gnc_commoditylist *clist);
int rpcend_do_add_acct (AccountGroup *ag, gncAccount * acct,
gnc_commodity_table *ct);
int rpcend_do_add_txn (gncTransaction * txn, gnc_commodity_table *ct);
void rpcend_build_gncquery (gncQuery *gq, Query *q);
void rpcend_parse_gncquery (gncQuery *gq, Query *q);
void rpcend_free_gncquery (gncQuery *gq);
void rpcend_free_query (Query *q);
#endif /* __RPC_UTILS_H */

418
src/engine/rpc/clnt_thrd.c Normal file
View File

@ -0,0 +1,418 @@
/* @(#)clnt_tcp.c 2.2 88/08/01 4.0 RPCSRC */
/*
* Sun RPC is a product of Sun Microsystems, Inc. and is provided for
* unrestricted use provided that this legend is included on all tape
* media and as a part of the software program in whole or part. Users
* may copy or modify Sun RPC without charge, but are not authorized
* to license or distribute it to anyone else except as part of a product or
* program developed by the user.
*
* SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
* WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
* PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
*
* Sun RPC is provided with no support and without any obligation on the
* part of Sun Microsystems, Inc. to assist in its use, correction,
* modification or enhancement.
*
* SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
* INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
* OR ANY PART THEREOF.
*
* In no event will Sun Microsystems, Inc. be liable for any lost revenue
* or profits or other special, indirect and consequential damages, even if
* Sun has been advised of the possibility of such damages.
*
* Sun Microsystems, Inc.
* 2550 Garcia Avenue
* Mountain View, California 94043
*/
#if !defined(lint) && defined(SCCSIDS)
static char sccsid[] = "@(#)clnt_tcp.c 1.37 87/10/05 Copyr 1984 Sun Micro";
#endif
/*
* clnt_thrd.c, Implements a Threaded, TCP/IP based, client side RPC.
*
* Copyright (C) 1984, Sun Microsystems, Inc.
*
* TCP based RPC supports 'batched calls'.
* A sequence of calls may be batched-up in a send buffer. The rpc call
* return immediately to the client even though the call was not necessarily
* sent. The batching occurs if the results' xdr routine is NULL (0) AND
* the rpc timeout value is zero (see clnt.h, rpc).
*
* Clients should NOT casually batch calls that in fact return results; that is,
* the server side should be aware that a call is batched and not produce any
* return message. Batched calls that produce many result messages can
* deadlock (netlock) the client and the server....
*
* Now go hang yourself.
*/
#include <netdb.h>
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
#include <rpc/rpc.h>
#include <sys/poll.h>
#include <sys/socket.h>
#include <rpc/pmap_clnt.h>
#include "xprt_thrd.h"
#include "clnt_thrd.h"
#define MCALL_MSG_SIZE 24
struct ct_data
{
bool_t ct_waitset; /* wait set by clnt_control? */
struct timeval ct_wait;
struct rpc_err ct_error;
char ct_mcall[MCALL_MSG_SIZE]; /* marshalled callmsg header */
u_int ct_mpos; /* length of marshalled header */
u_long ct_prog; /* Program number */
u_long ct_vers; /* Version Number */
TXPRT_WAIT *ct_repl; /* A reply wait object */
TXPRT *ct_xprt; /* Transport Object */
};
static enum clnt_stat clntthrd_call (CLIENT *, u_long, xdrproc_t, caddr_t,
xdrproc_t, caddr_t, struct timeval);
static void clntthrd_abort (void);
static void clntthrd_geterr (CLIENT *, struct rpc_err *);
static bool_t clntthrd_freeres (CLIENT *, xdrproc_t, caddr_t);
static bool_t clntthrd_control (CLIENT *, int, char *);
static void clntthrd_destroy (CLIENT *);
static struct clnt_ops thrd_ops =
{
clntthrd_call,
clntthrd_abort,
clntthrd_geterr,
clntthrd_freeres,
clntthrd_destroy,
clntthrd_control
};
/*
* Create a client handle for a threaded tcp/ip connection.
* transport is the Threaded Transport we wish to attach this client to.
* NB: It is the client's responsibility to open and close the transport
* NB: The rpch->cl_auth is set null authentication. Caller may wish to set
* this to something more useful.
*/
CLIENT *
clntthrd_create (TXPRT *transport, u_long prog, u_long vers)
{
CLIENT *h = NULL;
struct ct_data *ct = NULL;
struct rpc_msg call_msg;
struct timeval now;
XDR xdr;
if (transport == NULL)
return (CLIENT *)NULL;
h = (CLIENT *) malloc (sizeof (*h));
if (h == NULL)
{
(void) fprintf (stderr, "clntthrd_create: out of memory\n");
goto fooy;
}
ct = (struct ct_data *) malloc (sizeof (*ct));
if (ct == NULL)
{
(void) fprintf (stderr, "clntthrd_create: out of memory\n");
goto fooy;
}
memset (ct, 0, sizeof (*ct));
memset (h, 0, sizeof (*h));
/*
* Set up private data structure
*/
ct->ct_repl = xprt_thrd_new_wait ();
if (ct->ct_repl == NULL)
{
(void) fprintf (stderr, "clntthrd_create: out of memory\n");
goto fooy;
}
ct->ct_wait.tv_usec = 0;
ct->ct_waitset = FALSE;
ct->ct_xprt = transport;
/*
* Initialize call message
*/
gettimeofday (&now, NULL);
srand48 (now.tv_sec ^ now.tv_usec);
call_msg.rm_xid = lrand48();
call_msg.rm_direction = CALL;
call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION;
ct->ct_prog = call_msg.rm_call.cb_prog = prog;
ct->ct_vers = call_msg.rm_call.cb_vers = vers;
/*
* pre-serialize the static part of the call msg and stash it away
*/
xdrmem_create (&xdr, ct->ct_mcall, MCALL_MSG_SIZE, XDR_ENCODE);
if (!xdr_callhdr (&xdr, &call_msg))
{
goto fooy;
}
ct->ct_mpos = XDR_GETPOS (&xdr);
XDR_DESTROY (&xdr);
/*
* Build the CLIENT structure
*/
h->cl_auth = authnone_create ();
h->cl_ops = &thrd_ops;
h->cl_private = (caddr_t) ct;
return h;
fooy:
/*
* Something goofed, free stuff and barf
*/
if (ct != NULL) {
if (ct->ct_repl != NULL)
xprt_destroy_wait (ct->ct_repl);
free ((caddr_t) ct);
}
if (h != NULL) free ((caddr_t) h);
return ((CLIENT *) NULL);
}
static enum clnt_stat
clntthrd_call (h, proc, xdr_args, args_ptr, xdr_results, results_ptr, timeout)
CLIENT *h;
u_long proc;
xdrproc_t xdr_args;
caddr_t args_ptr;
xdrproc_t xdr_results;
caddr_t results_ptr;
struct timeval timeout;
{
struct ct_data *ct = (struct ct_data *) h->cl_private;
XDR *xdrs;
struct rpc_msg reply_msg;
u_long x_id;
u_int32_t *msg_x_id = (u_int32_t *) (ct->ct_mcall); /* yuk */
bool_t shipnow;
int refreshes = 2;
if (!ct->ct_waitset)
{
ct->ct_wait = timeout;
}
shipnow =
(xdr_results == (xdrproc_t) 0 && timeout.tv_sec == 0
&& timeout.tv_usec == 0) ? FALSE : TRUE;
/* Obtain and lock XDR object */
xdrs = TXPRT_GET_XDR (ct->ct_xprt);
call_again:
xdrs->x_op = XDR_ENCODE;
ct->ct_error.re_status = RPC_SUCCESS;
x_id = ntohl (--(*msg_x_id));
if ((!XDR_PUTBYTES (xdrs, ct->ct_mcall, ct->ct_mpos)) ||
(!XDR_PUTLONG (xdrs, (long *) &proc)) ||
(!AUTH_MARSHALL (h->cl_auth, xdrs)) ||
(!(*xdr_args) (xdrs, args_ptr)))
{
if (ct->ct_error.re_status == RPC_SUCCESS)
ct->ct_error.re_status = RPC_CANTENCODEARGS;
(void) xdrrec_endofrecord (xdrs, TRUE);
TXPRT_REL_XDR (ct->ct_xprt);
return (ct->ct_error.re_status);
}
if (!xdrrec_endofrecord (xdrs, shipnow)) {
TXPRT_REL_XDR (ct->ct_xprt);
return ct->ct_error.re_status = RPC_CANTSEND;
}
if (!shipnow) {
TXPRT_REL_XDR (ct->ct_xprt);
return RPC_SUCCESS;
}
/*
* Hack to provide rpc-based message passing
*/
if (timeout.tv_sec == 0 && timeout.tv_usec == 0)
{
TXPRT_REL_XDR (ct->ct_xprt);
return ct->ct_error.re_status = RPC_TIMEDOUT;
}
/* Setup reply-state for Transport callback */
ct->ct_repl->tw_x_id = x_id;
reply_msg.acpted_rply.ar_verf = _null_auth;
reply_msg.acpted_rply.ar_results.where = NULL;
reply_msg.acpted_rply.ar_results.proc = (xdrproc_t)xdr_void;
/* Now register the callback and wait for our reply message */
TXPRT_WAIT_REPLY (ct->ct_xprt, &reply_msg, &xdrs, ct->ct_repl, timeout);
/*
* Ok, we're back. That means that our reply has come in. We expect
* that reply_msg and xdrs have been filled in for us, and the XDR
* Object is locked. That means we don't have to look for the response
* on our own, nor lock XDR on our own, so....
*/
/*
* process header
*/
_seterr_reply (&reply_msg, &(ct->ct_error));
if (ct->ct_error.re_status == RPC_SUCCESS)
{
if (!AUTH_VALIDATE (h->cl_auth, &reply_msg.acpted_rply.ar_verf))
{
ct->ct_error.re_status = RPC_AUTHERROR;
ct->ct_error.re_why = AUTH_INVALIDRESP;
}
else if (!(*xdr_results) (xdrs, results_ptr))
{
if (ct->ct_error.re_status == RPC_SUCCESS)
ct->ct_error.re_status = RPC_CANTDECODERES;
}
/* free verifier ... */
if (reply_msg.acpted_rply.ar_verf.oa_base != NULL)
{
xdrs->x_op = XDR_FREE;
(void) xdr_opaque_auth (xdrs, &(reply_msg.acpted_rply.ar_verf));
}
} /* end successful completion */
else
{
/* maybe our credentials need to be refreshed ... */
if (refreshes-- && AUTH_REFRESH (h->cl_auth))
/* We still have the XDR lock */
goto call_again;
} /* end of unsuccessful completion */
TXPRT_REL_XDR (ct->ct_xprt);
return ct->ct_error.re_status;
}
static void
clntthrd_geterr (h, errp)
CLIENT *h;
struct rpc_err *errp;
{
struct ct_data *ct =
(struct ct_data *) h->cl_private;
*errp = ct->ct_error;
}
static bool_t
clntthrd_freeres (cl, xdr_res, res_ptr)
CLIENT *cl;
xdrproc_t xdr_res;
caddr_t res_ptr;
{
bool_t res;
struct ct_data *ct = (struct ct_data *) cl->cl_private;
XDR *xdrs = TXPRT_GET_XDR (ct->ct_xprt);
xdrs->x_op = XDR_FREE;
res = (*xdr_res) (xdrs, res_ptr);
TXPRT_REL_XDR (ct->ct_xprt);
return res;
}
static void
clntthrd_abort ()
{
}
static bool_t
clntthrd_control (CLIENT *cl, int request, char *info)
{
struct ct_data *ct = (struct ct_data *) cl->cl_private;
switch (request)
{
case CLSET_TIMEOUT:
ct->ct_wait = *(struct timeval *) info;
ct->ct_waitset = TRUE;
break;
case CLGET_TIMEOUT:
*(struct timeval *) info = ct->ct_wait;
break;
case CLGET_XID:
/*
* use the knowledge that xid is the
* first element in the call structure *.
* This will get the xid of the PREVIOUS call
*/
*(u_long *)info = ntohl (*(u_long *)ct->ct_mcall);
break;
case CLSET_XID:
/* This will set the xid of the NEXT call */
*(u_long *)ct->ct_mcall = htonl (*(u_long *)info - 1);
/* decrement by 1 as clntthrd_call() increments once */
case CLGET_VERS:
/*
* This RELIES on the information that, in the call body,
* the version number field is the fifth field from the
* begining of the RPC header. MUST be changed if the
* call_struct is changed
*/
*(u_long *)info = ntohl (*(u_long *)(ct->ct_mcall +
4 * BYTES_PER_XDR_UNIT));
break;
case CLSET_VERS:
*(u_long *)(ct->ct_mcall + 4 * BYTES_PER_XDR_UNIT)
= htonl (*(u_long *)info);
break;
case CLGET_PROG:
/*
* This RELIES on the information that, in the call body,
* the program number field is the field from the
* begining of the RPC header. MUST be changed if the
* call_struct is changed
*/
*(u_long *)info = ntohl(*(u_long *)(ct->ct_mcall +
3 * BYTES_PER_XDR_UNIT));
break;
case CLSET_PROG:
*(u_long *)(ct->ct_mcall + 3 * BYTES_PER_XDR_UNIT)
= htonl(*(u_long *)info);
break;
/* We don't support these, yet */
case CLSET_FD_CLOSE:
case CLSET_FD_NCLOSE:
case CLGET_SERVER_ADDR:
case CLGET_FD:
return FALSE;
/* The following are only possible with TI-RPC */
case CLGET_RETRY_TIMEOUT:
case CLSET_RETRY_TIMEOUT:
case CLGET_SVC_ADDR:
case CLSET_SVC_ADDR:
case CLSET_PUSH_TIMOD:
case CLSET_POP_TIMOD:
default:
return FALSE;
}
return TRUE;
}
static void
clntthrd_destroy (CLIENT *h)
{
struct ct_data *ct = (struct ct_data *) h->cl_private;
xprt_destroy_wait (ct->ct_repl);
free ((caddr_t) ct);
free ((caddr_t) h);
}

View File

@ -0,0 +1,16 @@
/*
* clnt_thrd.h -- Threaded, Multiplexed, RPC/TCP Client
*
* Written By: Derek Atkins <warlord@MIT.EDU>
*
*/
#ifndef RPC_CLNT_THRD_H
#define RPC_CLNT_THRD_H
#include <rpc/clnt.h>
#include "xprt_thrd.h"
CLIENT *clntthrd_create (TXPRT *transport, u_long prog, u_long vers);
#endif /* RPC_CLNT_THRD_H */

View File

@ -0,0 +1,62 @@
/*
* FILE:
* gncAccount.x
*
* FUNCTION:
* The RPC definition for a Gnucash Account
*
* HISTORY:
* Created By: Derek Atkins <warlord@MIT.EDU>
* Copyright (c) 2001, Derek Atkins
*/
#ifndef __GNC_ACCOUNT_X
#define __GNC_ACCOUNT_X
#include "gncKVP.x"
#include "gncGUID.x"
#include "gncCommodity.x"
#ifdef RPC_HDR
%#include "Account.h"
#endif
struct gncAccount {
gncGUID guid;
string name<>;
string code<>;
string desc<>;
gnc_kvp_frame * kvp_data;
enum_t type; /* GNCAccountType */
/* This really sucks -- why do I need to send both a currency and a
* security? Well, the engine seems to want it that way. Ouch.
*/
#ifdef GNCACCT_COMMODITY
gncCommodityPtr commodity;
#else
gncCommodityPtr currency;
gncCommodityPtr security;
#endif
gncGUID * parent;
int vers;
gncNumeric balance;
gncNumeric cleared_balance;
gncNumeric reconciled_balance;
gncNumeric share_balance;
gncNumeric share_cleared_balance;
gncNumeric share_reconciled_balance;
bool core_dirty;
bool do_free;
};
struct gnc_acctlist {
gncAccount * acct;
gnc_acctlist * next;
};
#endif /* __GNC_ACCOUNT_X */

View File

@ -0,0 +1,43 @@
/*
* FILE:
* gncCommodity.x
*
* FUNCTION:
* The RPC definition for a Gnucash Commodity
*
* HISTORY:
* Created By: Derek Atkins <warlord@MIT.EDU>
* Copyright (c) 2001, Derek Atkins
*/
#ifndef __GNC_COMMODITY_X
#define __GNC_COMMODITY_X
/* This is an actual commodity entry; unique_name is generated */
struct gncCommodity {
string fullname<>;
string namespace<>;
string mnemonic<>;
string printname<>;
string exchange_code<>;
int fraction;
};
/* List of commodities */
struct gnc_commoditylist {
gncCommodity * commodity;
gnc_commoditylist * next;
};
/* This is sufficient information to find a commodity in the commodity
* table using gnc_commodity_table_lookup(), although while it does
* save on network transfer space, it makes it harder on the sender
* to actually package up an account. So let's keep it here but not
* use it.
*/
struct gncCommodityPtr {
string namespace<>;
string mnemonic<>;
};
#endif /* __GNC_COMMODITY_X */

34
src/engine/rpc/gncGUID.x Normal file
View File

@ -0,0 +1,34 @@
/*
* FILE:
* gncQuery.x
*
* FUNCTION:
* The RPC definition for a Gnucash Query
*
* HISTORY:
* Created By: Derek Atkins <warlord@MIT.EDU>
* Copyright (c) 2001, Derek Atkins
*/
#ifndef __GNC_GUID_X
#define __GNC_GUID_X
typedef opaque gncGUID[16];
struct gnc_guidlist {
gncGUID * guid;
gnc_guidlist * next;
};
struct gnc_vers_list {
gncGUID * guid;
int vers;
gnc_vers_list * next;
};
struct gncTimespec {
int64_t tv_sec;
int tv_nsec;
};
#endif /* __GNC_GUID_X */

61
src/engine/rpc/gncKVP.x Normal file
View File

@ -0,0 +1,61 @@
/*
* FILE:
* gncKVP.x
*
* FUNCTION:
* The RPC definition for a Gnucash kvp_frame
*
* HISTORY:
* Created By: Derek Atkins <warlord@MIT.EDU>
* Copyright (c) 2001, Derek Atkins
*/
#ifndef __GNC_KVP_X
#define __GNC_KVP_X
#include "gncGUID.x"
#ifdef RPC_HDR
%#include "kvp_frame.h"
#endif
struct gncNumeric {
int64_t num;
int64_t denom;
};
struct gnc_kvp_valuelist {
struct gnc_kvp_value * val;
gnc_kvp_valuelist * next;
};
union gnc_kvp_value switch (enum_t type) { /* kvp_value_t */
case KVP_TYPE_GINT64:
int64_t int64;
case KVP_TYPE_DOUBLE:
double dbl;
case KVP_TYPE_NUMERIC:
gncNumeric numeric;
case KVP_TYPE_STRING:
string str<>;
case KVP_TYPE_GUID:
gncGUID guid;
case KVP_TYPE_BINARY:
opaque binary<>;
case KVP_TYPE_GLIST:
gnc_kvp_valuelist * glist;
case KVP_TYPE_FRAME:
struct gnc_kvp_frame * frame;
};
struct gnc_kvp {
string key<>;
gnc_kvp_value * value;
};
struct gnc_kvp_frame {
gnc_kvp * data;
gnc_kvp_frame * next;
};
#endif /* __GNC_KVP_X */

18
src/engine/rpc/gncPrice.x Normal file
View File

@ -0,0 +1,18 @@
/*
* FILE:
* gncPrice.x
*
* FUNCTION:
* The RPC protocol definition for Gnucash PriceDB
*
* HISTORY:
* Created By: Derek Atkins <warlord@MIT.EDU>
* Copyright (c) 2001, Derek Atkins
*/
#ifndef __GNC_PRICE_X
#define __GNC_PRICE_X
#endif /* __GNC_PRICE_X */

View File

@ -0,0 +1,90 @@
module Query {
enum SortBy { STANDARD, DATE, DATEENTERED, DATERECONCILED, NUM,
AMOUNT, MEMO, DESC, RECONCILE, NONE };
enum QueryOp { AND, OR, NAND, NOR, XOR };
enum pdtypet { DATE, AMOUNT, ACCOUNT, STRING, CLEARED, MISC };
enum acctmatcht { ACCT_MATCH_ALL, ACCT_MATCH_ANY, ACCT_MATCH_NONE }
enum amtmatcht { AMT_MATCH_ATLEAST, AMT_MATCH_ATMOST,
AMT_MATCH_EXACTLY } ;
enum amtmatchsgnt { AMT_SGN_MATCH_EITHER, AMT_SGN_MATCH_CREDIT,
AMT_SGN_MATCH_DEBIT } ;
enum clearing { CLEARED_NO, CLEARED_CLEARED, CLEARED_RECONCILED,
CLEARED_FROZEN };
enum strmatching { STRING_MATCH_CASE, STRING_MATCH_REGEXP,
STRING_MATCH_INSENSITIVE };
struct DatePredicateData {
long usestart;
Timespec start;
long useend;
Timespec end;
}
struct AmountPredicateData {
amtmatcht how;
amtmatchsgnt amt_sgn;
double amount;
};
struct AccountPredicateData {
acctmatcht how;
Accounts::AccountList accounts;
// Note that this references a type from
// module "Accounts"
}
struct StringPredicateData {
strmatching behavior; /* what kind of search? */
string matchstring;
// regex_t compiled; // Compilation isn't part of the interface
} StringPredicateData;
struct MiscPredicateData {
long how; /* Shouldn't this use one of the enums? */
long data; /* What is this? */
};
struct ClearedPredicateData {
long how;
};
union PredicateData switch (pdtypet) {
case DATE:
DatePredicateData date;
case AMOUNT:
AmountPredicateData amount;
case ACCOUNT:
AccountPredicateData acct;
case STRING:
StringPredicateData str;
case CLEARED:
ClearedPredicateData cleared;
case MISC:
MiscPredicateData misc;
}
struct QueryTerm {
Predicate p; /* What is this type supposed to be? */
PredicateData data;
long sense;
};
/* Something is wrong with the following; I am somehow not correctly
understanding what the Predicate type is... I'll punt and
pretend that a Query consists of a binary tree of QueryTerm
elements... */
struct Query {
QueryTerm qt;
QueryOp combinethus;
Query subquery;
};
// Locally, there would be the whole host of "Query Construction"
// functions to build up a complex query.
void InvokeQuery (in query q,
out Transactions::TransactionList txns);
/* Note that this references, from the "Transactions" module, the
"TransactionList" typedef, which would reflect a sequence of
transactions... */
};

134
src/engine/rpc/gncQuery.x Normal file
View File

@ -0,0 +1,134 @@
/*
* FILE:
* gncQuery.x
*
* FUNCTION:
* The RPC definition for a Gnucash Query
*
* HISTORY:
* Created By: Derek Atkins <warlord@MIT.EDU>
* Copyright (c) 2001, Derek Atkins
*/
#ifndef __GNC_QUERY_X
#define __GNC_QUERY_X
#include "gncAccount.x"
#include "gncGUID.x"
#ifdef RPC_HDR
%#include "Query.h"
#endif
struct gncDatePredicateData {
enum_t term_type; /* pr_type_t */
int sense;
int usestart;
gncTimespec start;
int useend;
gncTimespec end;
};
struct gncAmountPredicateData {
enum_t term_type; /* pr_type_t */
int sense;
enum_t how; /* amt_match_t */
enum_t amt_sgn; /* amt_match_sgn_t */
double amount;
};
struct gncAccountPredicateData {
enum_t term_type; /* pr_type_t */
int sense;
enum_t how; /* acct_match_t */
/* Leave out Accts; we can recreate on the server side */
gnc_guidlist * acct_guids;
};
struct gncStringPredicateData {
enum_t term_type; /* pr_type_t */
int sense;
int case_sens;
int use_regexp;
string matchstring<>;
/* XXX: How do I transfer a regex_t? */
};
struct gncClearedPredicateData {
enum_t term_type; /* pr_type_t */
int sense;
enum_t how; /* cleared_match_t */
};
struct gncBalancePredicateData {
enum_t term_type; /* pr_type_t */
int sense;
enum_t how; /* balance_match_t */
};
struct gncMiscPredicateData {
enum_t term_type; /* pr_type_t */
int sense;
int how;
int data;
};
union gncPredicateData switch (enum_t type) { /* pd_type_t */
case PD_DATE:
gncDatePredicateData date;
case PD_AMOUNT:
gncAmountPredicateData amount;
case PD_ACCOUNT:
gncAccountPredicateData acct;
case PD_STRING:
gncStringPredicateData str;
case PD_CLEARED:
gncClearedPredicateData cleared;
case PD_BALANCE:
gncBalancePredicateData balance;
case PD_MISC:
gncMiscPredicateData misc;
};
struct gncQueryTerm {
gncPredicateData data;
int * p; /* This 'predicate' is just for compatibility
* with Query.h. We should really check if
* it's non-NULL, because we cannot easily
* transfer a function-pointer!
*/
};
/* A list of QueryTerms */
struct gncQTList {
gncQueryTerm * qt;
gncQTList * next;
};
/* A list of QueryTerm Lists */
struct gncQTOrlist {
gncQTList * andlist;
gncQTOrlist * next;
};
/* See Query.c for the description */
struct gncQuery {
gncQTOrlist * terms;
enum_t primary_sort; /* sort_type_t */
enum_t secondary_sort; /* sort_type_t */
enum_t tertiary_sort; /* sort_type_t */
bool primary_increasing;
bool secondary_increasing;
bool tertiary_increasing;
int max_splits;
/* Results cache */
int changed;
enum_t last_run_type; /* query_run_t */
int * acct_group; /* AccountGroup * */
int * split_list; /* GList * */
int * xtn_list; /* GList * */
};
#endif /* __GNC_QUERY_X */

140
src/engine/rpc/gncRpc.x Normal file
View File

@ -0,0 +1,140 @@
/*
* FILE:
* gncRpc.x
*
* FUNCTION:
* The RPC protocol definition for Gnucash
*
* HISTORY:
* Created By: Derek Atkins <warlord@MIT.EDU>
* Copyright (c) 2001, Derek Atkins
*/
#ifdef RPC_XDR
%#ifndef xdr_enum_t
%#define xdr_enum_t xdr_enum
%#endif
#endif
#include "gncQuery.x"
#include "gncAccount.x"
#include "gncTxn.x"
typedef opaque gncrpc_ptr[8];
/***************************************************************/
struct gncrpc_book_begin_args {
gncrpc_ptr book;
gncrpc_ptr backend;
string book_id<>;
bool ignore_lock;
bool create;
};
struct gncrpc_book_load_ret {
int error;
gnc_commoditylist * commodities; /* List of commodities */
gnc_acctlist * acctlist; /* Full Account tree */
};
struct gncrpc_backend_guid {
gncrpc_ptr backend;
gncGUID guid;
};
struct gncrpc_backend_txn {
gncrpc_ptr backend;
gncTransaction txn;
};
struct gncrpc_commit_acct_args {
gncrpc_ptr backend;
gncAccount acct;
#ifdef GNCACCT_COMMODITY
gncCommodity * commodity;
#else
gncCommodity * currency;
gncCommodity * security;
#endif
};
struct gncrpc_commit_txn_args {
gncrpc_ptr backend;
gncTransaction new;
};
struct gncrpc_sync1_args {
gncrpc_ptr backend;
gnc_commoditylist * commodities; /* all commodities */
gnc_vers_list * acctlist; /* list of all account guid+versions */
gnc_vers_list * txnlist; /* list of all txn guid+versions */
};
struct gncrpc_sync1_ret {
int error;
gnc_vers_list * send_acctlist; /* accts to send to server */
gnc_vers_list * send_txnlist; /* txns to send to server */
gnc_acctlist * acctlist; /* new or changed accounts from server */
gnc_txnlist * txnlist; /* new or changed txns from server */
gnc_commoditylist * commodities; /* new commodities from server */
};
struct gncrpc_sync2_args {
gncrpc_ptr backend;
gnc_acctlist * acctlist; /* new or changed accounts in client */
gnc_txnlist * txnlist; /* new or changed txns in client */
};
struct gncrpc_query_args {
gncrpc_ptr backend;
gncGUID * group_parent_guid;
gncQuery * query;
};
struct gncrpc_query_ret {
int error;
gnc_vers_list * txnlist;
};
struct gncrpc_get_txns_args {
gncrpc_ptr backend;
gnc_vers_list * guids;
};
struct gncrpc_get_txns_ret {
int error;
gnc_txnlist * txnlist;
};
/***************************************************************/
program GNCRPC_PROG {
version GNCRPC_VERS {
/*
* int gncrpc_version ()
* send the min/max version and get back a suggested version number
*/
int gncrpc_version () = 0;
/* Backend functions */
int gncrpc_book_begin (gncrpc_book_begin_args args) = 1;
gncrpc_book_load_ret gncrpc_book_load (gncrpc_ptr backend) = 2;
int gncrpc_book_end (gncrpc_ptr backend) = 3;
int gncrpc_account_begin_edit (gncrpc_backend_guid) = 4;
int gncrpc_account_commit_edit (gncrpc_commit_acct_args) = 5;
int gncrpc_account_rollback_edit (gncrpc_backend_guid) = 6;
int gncrpc_txn_begin_edit (gncrpc_backend_guid) = 7;
int gncrpc_txn_commit_edit (gncrpc_commit_txn_args) = 8;
int gncrpc_txn_rollback_edit (gncrpc_backend_guid) = 9;
gncrpc_query_ret gncrpc_run_query (gncrpc_query_args) = 10;
gncrpc_sync1_ret gncrpc_sync1 (gncrpc_sync1_args) = 11;
int gncrpc_sync2 (gncrpc_sync2_args) = 12;
/* Helper functions */
gncrpc_get_txns_ret gncrpc_get_txns (gncrpc_get_txns_args) = 13;
} = 1;
} = 729284;

View File

@ -0,0 +1,568 @@
/*
* FILE:
* gncRpc_server.c
*
* FUNCTION:
* Implements the Gnucash RPC server stubs, as generated by rpcgen.
* Note: The function prototypes here must match those created by
* rpcgen -- if any function prototypes in gncRpc.x are changed, this
* file must change as well.
*
* HISTORY:
* Created By: Derek Atkins <warlord@MIT.EDU>
* Copyright (c) 2001, Derek Atkins
*/
#include "gncRpc.h"
#include "RpcServerP.h"
#include "RpcUtils.h"
#include "TransactionP.h"
#include "AccountP.h"
#include "gnc-engine-util.h"
static short module = MOD_BACKEND;
static bool_t gncrpc_get_state (struct svc_req *req, TXPRT **xprt,
GncRpcSvc **cl)
{
TXPRT *x;
bool_t retval;
retval = svc_getargs (req->rq_xprt, (xdrproc_t) xprt_thrd_getargs_hook,
(caddr_t) &x);
if (!retval)
return !retval;
if (xprt)
*xprt = x;
if (cl) {
RPCSock *s = (RPCSock *) TXPRT_GETSOCK (x);
*cl = (GncRpcSvc *) RpcGetData (s);
}
return retval;
}
bool_t
gncrpc_version_1_svc(void *argp, int *result, struct svc_req *rqstp)
{
bool_t retval = TRUE;
*result = GNCRPC_PROTOCOL_VERSION;
return retval;
}
bool_t
gncrpc_book_begin_1_svc(gncrpc_book_begin_args *argp, int *result, struct svc_req *rqstp)
{
bool_t retval = TRUE;
GncRpcSvc *cl;
int res = ERR_RPC_SERVER_STATE;
ENTER ("id=\"%s\"", argp->book_id);
do {
gboolean ret;
retval = gncrpc_get_state (rqstp, NULL, &cl);
if (!retval)
break;
ret = gnc_book_begin (cl->book, argp->book_id, argp->ignore_lock,
argp->create);
PINFO ("ret == %s", (ret == TRUE ? "true" : "false"));
res = gnc_book_pop_error (cl->book);
} while (0);
*result = res;
LEAVE ("done %d", res);
return retval;
}
bool_t
gncrpc_book_load_1_svc(char *argp, gncrpc_book_load_ret *result, struct svc_req *rqstp)
{
bool_t retval = TRUE;
GncRpcSvc *cl;
gboolean ret;
ENTER ("ok");
memset (result, 0, sizeof (*result));
retval = gncrpc_get_state (rqstp, NULL, &cl);
if (!retval) {
result->error = ERR_RPC_SERVER_STATE;
LEAVE ("bad state");
return retval;
}
ret = gnc_book_load (cl->book);
if (!ret) {
result->error = gnc_book_pop_error (cl->book);
} else {
result->commodities =
rpcend_build_gnccommoditylist (gnc_book_get_commodity_table
(cl->book),
TRUE);
result->acctlist =
rpcend_build_gncacctlist (gnc_book_get_group (cl->book));
}
LEAVE ("res = %d", result->error);
return retval;
}
bool_t
gncrpc_book_end_1_svc(char *argp, int *result, struct svc_req *rqstp)
{
bool_t retval = TRUE;
GncRpcSvc *cl;
ENTER ("ok");
retval = gncrpc_get_state (rqstp, NULL, &cl);
if (!retval) {
*result = ERR_RPC_SERVER_STATE;
LEAVE ("bad state");
return retval;
}
gnc_book_end (cl->book);
*result = gnc_book_pop_error (cl->book);
LEAVE ("res = %d", *result);
return retval;
}
bool_t
gncrpc_account_begin_edit_1_svc(gncrpc_backend_guid *argp, int *result, struct svc_req *rqstp)
{
bool_t retval = TRUE;
Account *acc;
GUID *guid = (GUID *) &(argp->guid);
ENTER ("ok");
acc = xaccAccountLookup (guid);
if (!acc) {
*result = -1; /* XXX Deal with new accounts */
LEAVE ("no acc");
return retval;
}
xaccAccountBeginEdit (acc);
*result = 0; /* XXX book_error */
LEAVE ("acct=%p (%s)", acc, acc ? acc->accountName : "");
return retval;
}
bool_t
gncrpc_account_commit_edit_1_svc(gncrpc_commit_acct_args *argp, int *result, struct svc_req *rqstp)
{
bool_t retval = TRUE;
Account *acc;
AccountGroup *ag;
gnc_commodity_table *ct;
GUID *guid = (GUID *) &(argp->acct.guid);
GncRpcSvc *cl;
int added;
ENTER ("vers=%d", argp->acct.vers);
retval = gncrpc_get_state (rqstp, NULL, &cl);
if (!retval) {
*result = ERR_RPC_SERVER_STATE;
LEAVE ("bad state");
return retval;
}
ag = gnc_book_get_group (cl->book);
ct = gnc_book_get_commodity_table (cl->book);
/* Load the commodity, in case it changed */
#ifdef GNCACCT_COMMODITY
rpcend_load_gnccommodity (ct, argp->commodity);
#else
rpcend_load_gnccommodity (ct, argp->currency);
rpcend_load_gnccommodity (ct, argp->security);
#endif
/* Now add the account */
added = rpcend_do_add_acct (ag, &(argp->acct), ct);
acc = xaccAccountLookup (guid);
if (!acc) {
*result = gnc_book_pop_error (cl->book);
if (*result == 0)
*result = ERR_RPC_FAILED;
LEAVE ("no acc");
return retval;
}
PINFO ("commiting acct..");
xaccAccountCommitEdit (acc);
LEAVE ("vers=%d, added=%d (%s)", acc->version, added,
acc ? acc->accountName : "");
*result = added < 0 ? 0 : ERR_RPC_NOT_ADDED; /* XXX book error? */
return retval;
}
bool_t
gncrpc_account_rollback_edit_1_svc(gncrpc_backend_guid *argp, int *result, struct svc_req *rqstp)
{
bool_t retval = TRUE;
Account *acc;
GUID *guid = (GUID *) &(argp->guid);
GncRpcSvc *cl;
ENTER ("ok");
retval = gncrpc_get_state (rqstp, NULL, &cl);
if (!retval) {
*result = ERR_RPC_SERVER_STATE;
LEAVE ("bad state");
return retval;
}
acc = xaccAccountLookup (guid);
if (!acc) {
*result = ERR_RPC_FAILED;
LEAVE ("no acc");
return retval;
}
/* XXX There is no account rollback. Just commit it without
* any changes */
xaccAccountCommitEdit (acc);
*result = gnc_book_pop_error (cl->book);
LEAVE ("acc=%p (%s)", acc, acc ? acc->accountName : "");
return retval;
}
bool_t
gncrpc_txn_begin_edit_1_svc(gncrpc_backend_guid *argp, int *result, struct svc_req *rqstp)
{
bool_t retval = TRUE;
Transaction *txn;
GUID *guid = (GUID *) &(argp->guid);
ENTER ("ok");
txn = xaccTransLookup (guid);
if (!txn) {
*result = -1; /* XXX: deal with 'new' transaction */
LEAVE ("no txn");
return retval;
}
xaccTransBeginEdit (txn);
*result = 0; /* XXX: book error */
LEAVE ("ok");
return retval;
}
bool_t
gncrpc_txn_commit_edit_1_svc(gncrpc_commit_txn_args *argp, int *result, struct svc_req *rqstp)
{
bool_t retval = TRUE;
Transaction *txn;
gnc_commodity_table *ct;
GUID *guid = (GUID *) &(argp->new.guid);
GncRpcSvc *cl;
int added;
ENTER ("vers=%d", argp->new.vers);
retval = gncrpc_get_state (rqstp, NULL, &cl);
if (!retval) {
*result = ERR_RPC_SERVER_STATE;
LEAVE ("bad state");
return retval;
}
ct = gnc_book_get_commodity_table (cl->book);
added = rpcend_do_add_txn (&(argp->new), ct);
txn = xaccTransLookup (guid);
if (!txn) {
*result = ERR_RPC_FAILED;
LEAVE ("no txn!");
return retval;
}
PINFO ("Committing txn..");
xaccTransCommitEdit (txn);
LEAVE ("ok, added=%d, vers=%d", added, txn->version);
*result = added < 0 ? 0 : ERR_RPC_NOT_ADDED; /* XXX book error? */
return retval;
}
bool_t
gncrpc_txn_rollback_edit_1_svc(gncrpc_backend_guid *argp, int *result, struct svc_req *rqstp)
{
bool_t retval = TRUE;
Transaction *txn;
GUID *guid = (GUID *) &(argp->guid);
ENTER ("ok");
txn = xaccTransLookup (guid);
if (!txn) {
*result = ERR_RPC_FAILED;
LEAVE ("no txn");
return retval;
}
xaccTransRollbackEdit (txn);
*result = 0; /* XXX book error? */
LEAVE ("ok");
return retval;
}
bool_t
gncrpc_run_query_1_svc(gncrpc_query_args *argp, gncrpc_query_ret *result, struct svc_req *rqstp)
{
bool_t retval = TRUE;
gncQuery thisq;
Query *q = (Query *)&thisq;
GList *txnlist;
GncRpcSvc *cl;
AccountGroup *ag = NULL;
ENTER ("parent=%p, q=%p", argp->group_parent_guid, argp->query);
memset (result, 0, sizeof (*result));
/* Find state */
retval = gncrpc_get_state (rqstp, NULL, &cl);
if (!retval) {
result->error = ERR_RPC_SERVER_STATE;
LEAVE ("Bad state");
return retval;
}
/* Figure out the query account group */
if (argp->group_parent_guid) {
Account *acc = xaccAccountLookup ((GUID *)argp->group_parent_guid);
if (acc)
ag = xaccAccountGetChildren (acc);
}
if (!ag)
ag = gnc_book_get_group (cl->book);
/* Setup query */
argp->query->acct_group = (int *)ag;
rpcend_parse_gncquery (argp->query, q);
argp->query->acct_group = NULL;
/* Run Query */
txnlist = xaccQueryGetTransactions (q, QUERY_MATCH_ANY);
/* Setup return value */
result->txnlist = rpcend_build_gncverslist_txn (txnlist, TRUE);
/* Free the txnlist */
g_list_free (txnlist);
/* Reset argument/query structure */
g_list_free ((GList *)(((gncQuery *)q)->split_list));
rpcend_free_query (q);
LEAVE ("query done");
return retval;
}
bool_t
gncrpc_sync1_1_svc(gncrpc_sync1_args *argp, gncrpc_sync1_ret *result, struct svc_req *rqstp)
{
bool_t retval = TRUE;
GncRpcSvc *cl;
AccountGroup *ag = NULL;
gnc_commodity_table *ct;
gnc_vers_list *this, *next, *new, **endnew, *old, **endold;
gnc_acctlist *acctlist = NULL;
gnc_txnlist *txnlist = NULL;
ENTER ("ok");
memset (result, 0, sizeof (*result));
retval = gncrpc_get_state (rqstp, NULL, &cl);
if (!retval) {
result->error = ERR_RPC_SERVER_STATE;
LEAVE ("bad state");
return retval;
}
ct = gnc_book_get_commodity_table (cl->book);
ag = gnc_book_get_group (cl->book);
rpcend_load_commoditylist (ct, argp->commodities);
/* Just send back the whole commodity list. It wont be too
* big, right???? Otherwise, I can implement an N^2 algorithm
* to figure out which commodities we need to send back...
*/
result->commodities = rpcend_build_gnccommoditylist (ct, TRUE);
/* Walk the acctlist and txnlist; for each one, if the client
* is newer, ask them to send it; if the server is newer, grab
* the new version
*/
new = NULL;
endnew = &new;
old = NULL;
endold = &old;
for (this = argp->acctlist; this; this = next) {
GUID *guid = (GUID *)(this->guid);
Account *acc = xaccAccountLookup (guid);
next = this->next;
if (!acc || acc->version < this->vers) {
/* Tell the client to send this account */
*endnew = this;
endnew = &(this->next);
} else {
/* Maybe send this account? */
if (acc->version < this->vers) {
gnc_acctlist *newaccl = malloc (sizeof (*newaccl));
gncAccount *newacc = malloc (sizeof (*newacc));
rpcend_build_gncacct (newacc, acc);
newaccl->acct = newacc;
newaccl->next = acctlist;
acctlist = newaccl;
}
*endold = this;
endold = &(this->next);
}
} /* for */
/* XXX Should we figure out if there are new accounts and send
* them?
*/
/* Be sure to NULL-terminate these lists! */
*endold = NULL;
*endnew = NULL;
/* Reset the args and set the reply */
result->send_acctlist = new;
result->acctlist = acctlist;
argp->acctlist = old;
/* Do a similar thing for transactions */
new = NULL;
endnew = &new;
old = NULL;
endold = &old;
for (this = argp->txnlist; this; this = next) {
GUID *guid = (GUID *)(this->guid);
Transaction *txn = xaccTransLookup (guid);
next = this->next;
if (!txn || txn->version < this->vers) {
/* Tell the client to send this account */
*endnew = this;
endnew = &(this->next);
} else {
/* Maybe send this txn? */
if (txn->version < this->vers) {
gnc_txnlist *newtxnl = malloc (sizeof (*newtxnl));
gncTransaction *newtxn = malloc (sizeof (*newtxn));
rpcend_build_gnctxn (newtxn, txn);
newtxnl->txn = newtxn;
newtxnl->next = txnlist;
txnlist = newtxnl;
}
*endold = this;
endold = &(this->next);
}
} /* for */
/* Be sure to NULL-terminate these lists! */
*endold = NULL;
*endnew = NULL;
/* Reset the args and set the reply */
result->send_txnlist = new;
result->txnlist = txnlist;
argp->txnlist = old;
LEAVE ("ok");
return retval;
}
bool_t
gncrpc_sync2_1_svc(gncrpc_sync2_args *argp, int *result, struct svc_req *rqstp)
{
bool_t retval = TRUE;
GncRpcSvc *cl;
AccountGroup *ag = NULL;
gnc_acctlist *acctlist = NULL;
gnc_txnlist *txnlist = NULL;
gnc_commodity_table *ct;
ENTER ("ok");
*result = 0;
retval = gncrpc_get_state (rqstp, NULL, &cl);
if (!retval) {
*result = ERR_RPC_SERVER_STATE;
LEAVE ("bad state");
return retval;
}
ag = gnc_book_get_group (cl->book);
ct = gnc_book_get_commodity_table (cl->book);
for (acctlist = argp->acctlist; acctlist; acctlist = acctlist->next) {
rpcend_do_add_acct (ag, acctlist->acct, ct);
}
for (txnlist = argp->txnlist; txnlist; txnlist = txnlist->next) {
rpcend_do_add_txn (txnlist->txn, ct);
}
gnc_book_save (cl->book);
*result = gnc_book_pop_error (cl->book);
/* XXX: clear results if error! */
LEAVE ("ok");
return retval;
}
bool_t
gncrpc_get_txns_1_svc(gncrpc_get_txns_args *argp, gncrpc_get_txns_ret *result, struct svc_req *rqstp)
{
bool_t retval = TRUE;
GncRpcSvc *cl;
AccountGroup *ag;
ENTER ("get txns");
memset (result, 0, sizeof (*result));
retval = gncrpc_get_state (rqstp, NULL, &cl);
if (!retval) {
result->error = ERR_RPC_SERVER_STATE;
LEAVE ("Bad state");
return retval;
}
ag = gnc_book_get_group (cl->book);
result->txnlist = rpcend_build_gnctxnlist_list (ag, argp->guids);
LEAVE ("ok");
return retval;
}
int
gncrpc_prog_1_freeresult (SVCXPRT *transp, xdrproc_t xdr_result, caddr_t result)
{
xdr_free (xdr_result, result);
/*
* Insert additional freeing code here, if needed
*/
return 1;
}

View File

@ -0,0 +1,175 @@
/*
* This is sample code generated by rpcgen.
* These are only templates and you can use them
* as a guideline for developing your own functions.
*/
#include "gncRpc.h"
bool_t
gncrpc_version_1_svc(void *argp, int *result, struct svc_req *rqstp)
{
bool_t retval;
/*
* insert server code here
*/
return retval;
}
bool_t
gncrpc_book_begin_1_svc(gncrpc_book_begin_args *argp, int *result, struct svc_req *rqstp)
{
bool_t retval;
/*
* insert server code here
*/
return retval;
}
bool_t
gncrpc_book_load_1_svc(char *argp, gncrpc_book_load_ret *result, struct svc_req *rqstp)
{
bool_t retval;
/*
* insert server code here
*/
return retval;
}
bool_t
gncrpc_book_end_1_svc(char *argp, int *result, struct svc_req *rqstp)
{
bool_t retval;
/*
* insert server code here
*/
return retval;
}
bool_t
gncrpc_account_begin_edit_1_svc(gncrpc_backend_guid *argp, int *result, struct svc_req *rqstp)
{
bool_t retval;
/*
* insert server code here
*/
return retval;
}
bool_t
gncrpc_account_commit_edit_1_svc(gncrpc_commit_acct_args *argp, int *result, struct svc_req *rqstp)
{
bool_t retval;
/*
* insert server code here
*/
return retval;
}
bool_t
gncrpc_txn_begin_edit_1_svc(gncrpc_backend_guid *argp, int *result, struct svc_req *rqstp)
{
bool_t retval;
/*
* insert server code here
*/
return retval;
}
bool_t
gncrpc_txn_commit_edit_1_svc(gncrpc_commit_txn_args *argp, int *result, struct svc_req *rqstp)
{
bool_t retval;
/*
* insert server code here
*/
return retval;
}
bool_t
gncrpc_txn_rollback_edit_1_svc(gncrpc_backend_guid *argp, int *result, struct svc_req *rqstp)
{
bool_t retval;
/*
* insert server code here
*/
return retval;
}
bool_t
gncrpc_run_query_1_svc(gncrpc_query_args *argp, gncrpc_query_ret *result, struct svc_req *rqstp)
{
bool_t retval;
/*
* insert server code here
*/
return retval;
}
bool_t
gncrpc_sync1_1_svc(gncrpc_sync1_args *argp, gncrpc_sync1_ret *result, struct svc_req *rqstp)
{
bool_t retval;
/*
* insert server code here
*/
return retval;
}
bool_t
gncrpc_sync2_1_svc(gncrpc_sync2_args *argp, int *result, struct svc_req *rqstp)
{
bool_t retval;
/*
* insert server code here
*/
return retval;
}
bool_t
gncrpc_get_txns_1_svc(gncrpc_get_txns_args *argp, gncrpc_get_txns_ret *result, struct svc_req *rqstp)
{
bool_t retval;
/*
* insert server code here
*/
return retval;
}
int
gncrpc_prog_1_freeresult (SVCXPRT *transp, xdrproc_t xdr_result, caddr_t result)
{
xdr_free (xdr_result, result);
/*
* Insert additional freeing code here, if needed
*/
return 1;
}

41
src/engine/rpc/gncSplit.x Normal file
View File

@ -0,0 +1,41 @@
/*
* FILE:
* gncSplit.x
*
* FUNCTION:
* The RPC protocol definition for Gnucash Splits
*
* HISTORY:
* Created By: Derek Atkins <warlord@MIT.EDU>
* Copyright (c) 2001, Derek Atkins
*/
#ifndef __GNC_SPLIT_X
#define __GNC_SPLIT_X
#include "gncGUID.x"
#include "gncKVP.x"
struct gncSplit {
gncGUID guid;
/* Different from Gnucash Split */
gncGUID acct_guid; /* Account GUID for this split */
gncGUID txn_guid; /* Parent transaction GUID */
string memo<>;
string action<>;
gnc_kvp_frame * kvp_data;
char reconciled;
gncTimespec date_reconciled;
gncNumeric value;
gncNumeric damount;
};
struct gnc_splitlist {
gncSplit * split;
gnc_splitlist * next;
};
#endif /* __GNC_SPLIT_X */

40
src/engine/rpc/gncTxn.x Normal file
View File

@ -0,0 +1,40 @@
/*
* FILE:
* gncTxn.x
*
* FUNCTION:
* The RPC protocol definition for Gnucash Transactions
*
* HISTORY:
* Created By: Derek Atkins <warlord@MIT.EDU>
* Copyright (c) 2001, Derek Atkins
*/
#ifndef __GNC_TXN_X
#define __GNC_TXN_X
#include "gncCommodity.x"
#include "gncGUID.x"
#include "gncKVP.x"
#include "gncSplit.x"
struct gncTransaction {
gncGUID guid;
gncTimespec date_entered;
gncTimespec date_posted;
string num<>;
string desc<>;
gnc_kvp_frame* kvp_data;
gncCommodityPtr common_currency;
int vers;
/* Different from here */
gnc_splitlist * splits;
bool do_free;
};
struct gnc_txnlist {
gncTransaction * txn;
gnc_txnlist * next;
};
#endif /* __GNC_TXN_X */

219
src/engine/rpc/svc_thrd.c Normal file
View File

@ -0,0 +1,219 @@
/* @(#)svc_tcp.c 2.2 88/08/01 4.0 RPCSRC */
/*
* Sun RPC is a product of Sun Microsystems, Inc. and is provided for
* unrestricted use provided that this legend is included on all tape
* media and as a part of the software program in whole or part. Users
* may copy or modify Sun RPC without charge, but are not authorized
* to license or distribute it to anyone else except as part of a product or
* program developed by the user.
*
* SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
* WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
* PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
*
* Sun RPC is provided with no support and without any obligation on the
* part of Sun Microsystems, Inc. to assist in its use, correction,
* modification or enhancement.
*
* SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
* INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
* OR ANY PART THEREOF.
*
* In no event will Sun Microsystems, Inc. be liable for any lost revenue
* or profits or other special, indirect and consequential damages, even if
* Sun has been advised of the possibility of such damages.
*
* Sun Microsystems, Inc.
* 2550 Garcia Avenue
* Mountain View, California 94043
*/
#if !defined(lint) && defined(SCCSIDS)
static char sccsid[] = "@(#)svc_tcp.c 1.21 87/08/11 Copyr 1984 Sun Micro";
#endif
/*
* svc_thrd.c, Server side for Threaded TCP/IP based RPC.
*
* Copyright (C) 1984, Sun Microsystems, Inc.
*
*/
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <rpc/rpc.h>
#include <sys/socket.h>
#include <sys/poll.h>
#include <errno.h>
#include <stdlib.h>
#include "xprt_thrd.h"
#include "svc_thrd.h"
#ifdef DEBUG_RPC
#define dfprintf(a) fprintf a
#else
#define dfprintf(a)
#endif
#ifdef USE_IN_LIBIO
# include <libio/iolibio.h>
# define fputs(s, f) _IO_fputs (s, f)
#endif
/*
* Ops vector for TCP/IP based rpc service handle
*/
static bool_t svcthrd_recv (SVCXPRT *, struct rpc_msg *);
static enum xprt_stat svcthrd_stat (SVCXPRT *);
static bool_t svcthrd_getargs (SVCXPRT *, xdrproc_t, caddr_t);
static bool_t svcthrd_reply (SVCXPRT *, struct rpc_msg *);
static bool_t svcthrd_freeargs (SVCXPRT *, xdrproc_t, caddr_t);
static void svcthrd_destroy (SVCXPRT *);
static const struct xp_ops svcthrd_op =
{
svcthrd_recv,
svcthrd_stat,
svcthrd_getargs,
svcthrd_reply,
svcthrd_freeargs,
svcthrd_destroy
};
struct tcp_conn
{ /* kept in xprt->xp_p1 */
u_long x_id;
TXPRT *xprt;
char verf_body[MAX_AUTH_BYTES];
};
/*
* Usage:
* xprt = svcthrd_create(transport);
*
* Creates and returns a (rpc) tcp based transporter.
* This routine returns a NULL if a problem occurred.
*
* NOTE: This assumes that a lock is held on the XDR Object whenever
* the methods herein are called. A word to the wise.... (The tranport
* object will hold this lock when it calls the dispatch routine for a call)
*/
SVCXPRT *
svcthrd_create (TXPRT *transport)
{
SVCXPRT *xprt;
struct tcp_conn *cd;
xprt = (SVCXPRT *) malloc (sizeof (SVCXPRT));
if (xprt == (SVCXPRT *) NULL)
{
(void) fputs ("svcthrd_create: out of memory\n", stderr);
goto done;
}
cd = (struct tcp_conn *) malloc (sizeof (struct tcp_conn));
if (cd == (struct tcp_conn *) NULL)
{
(void) fputs ("svcthrd_create: out of memory\n", stderr);
free ((char *) xprt);
xprt = (SVCXPRT *) NULL;
goto done;
}
memset (xprt, 0, sizeof (*xprt));
memset (cd, 0, sizeof (*cd));
cd->xprt = transport;
xprt->xp_p2 = NULL;
xprt->xp_p1 = (caddr_t) cd;
xprt->xp_verf.oa_base = cd->verf_body;
xprt->xp_addrlen = 0;
xprt->xp_ops = &svcthrd_op; /* truly deals with calls */
xprt->xp_port = 0; /* this is a connection, not a rendezvouser */
xprt->xp_sock = -1;
done:
return xprt;
}
static enum xprt_stat
svcthrd_stat (SVCXPRT *xprt)
{
return XPRT_IDLE;
}
static bool_t
svcthrd_recv (SVCXPRT *xprt, struct rpc_msg *msg)
{
struct tcp_conn *cd = (struct tcp_conn *) (xprt->xp_p1);
dfprintf ((stderr, "svcthrd_recv: set xid: %lx\n", msg->rm_xid));
cd->x_id = msg->rm_xid;
return TRUE;
}
/*
* NB: This method assumes that readlock is held, and it will release
* the locks on exit.
*
* This implies that the "caller" must already have the lock, and
* must only call this function once. NOTE that calling with
* xprt_thrd_getargs_hook will NOT touch the XDR object.
*/
static bool_t
svcthrd_getargs (SVCXPRT *xprt, xdrproc_t xdr_args, caddr_t args_ptr)
{
struct tcp_conn *cd = (struct tcp_conn *) (xprt->xp_p1);
XDR *xdr;
bool_t res;
if (xdr_args == (xdrproc_t) xprt_thrd_getargs_hook) {
if (args_ptr != NULL)
*((TXPRT **) args_ptr) = cd->xprt;
return TRUE;
}
xdr = TXPRT_GET_XDR (cd->xprt);
xdr->x_op = XDR_DECODE;
res = ((*xdr_args) (xdr, args_ptr));
TXPRT_REL_XDR (cd->xprt);
return res;
}
static bool_t
svcthrd_freeargs (SVCXPRT *xprt, xdrproc_t xdr_args, caddr_t args_ptr)
{
struct tcp_conn *cd = (struct tcp_conn *) (xprt->xp_p1);
XDR *xdrs = TXPRT_GET_XDR (cd->xprt);
bool_t res;
xdrs->x_op = XDR_FREE;
res = ((*xdr_args) (xdrs, args_ptr));
TXPRT_REL_XDR (cd->xprt);
return res;
}
static bool_t
svcthrd_reply (SVCXPRT *xprt, struct rpc_msg *msg)
{
struct tcp_conn *cd = (struct tcp_conn *) (xprt->xp_p1);
XDR *xdrs = TXPRT_GET_XDR (cd->xprt);
bool_t stat;
dfprintf ((stderr, "svcthrd_reply: xid=%lx (msg says %lx)\n", cd->x_id,
msg->rm_xid));
xdrs->x_op = XDR_ENCODE;
msg->rm_xid = cd->x_id;
stat = xdr_replymsg (xdrs, msg);
(void) xdrrec_endofrecord (xdrs, TRUE);
TXPRT_REL_XDR (cd->xprt);
return stat;
}
static void
svcthrd_destroy (SVCXPRT *xprt)
{
struct tcp_conn *cd = (struct tcp_conn *) xprt->xp_p1;
free ((caddr_t) cd);
free ((caddr_t) xprt);
}

16
src/engine/rpc/svc_thrd.h Normal file
View File

@ -0,0 +1,16 @@
/*
* svc_thrd.h -- Threaded, Multiplexed, RPC/TCP Service
*
* Written By: Derek Atkins <warlord@MIT.EDU>
*
*/
#ifndef RPC_SVC_THRD_H
#define RPC_SVC_THRD_H
#include <rpc/svc.h>
#include "xprt_thrd.h"
extern SVCXPRT *svcthrd_create (TXPRT *transport);
#endif /* RPC_SVC_THRD_H */

869
src/engine/rpc/xprt_thrd.c Normal file
View File

@ -0,0 +1,869 @@
/*
* Threaded TCP Transport scheme for RPC; allow a client and a service
* to share a single (TCP) stream. A single reader/writer thread
* exists to handle to I/O. Writers obtain a write-lock before
* writing to the stream, and 'readers' setup a callback with the I/O
* thread which wakes them up when a particular request (or reply) is
* received.
*
* Created by: Derek Atkins <warlord@MIT.EDU>
*
*/
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <rpc/rpc.h>
#include <sys/socket.h>
#include <sys/poll.h>
#include <errno.h>
#include <stdlib.h>
#include <pthread.h>
#include "xprt_thrd.h"
#include "clnt_thrd.h"
#include "svc_thrd.h"
#ifdef DEBUG_RPC
#define dfprintf(a) fprintf a
#else
#define dfprintf(a)
#endif
#define RQCRED_SIZE 400 /* this size is excessive */
/* Internal Service Request */
struct my_svc_req {
char cred[2 * MAX_AUTH_BYTES + RQCRED_SIZE];
struct svc_req r;
struct rpc_msg m;
void (*dispatch) (struct svc_req *r, SVCXPRT *x);
TXPRT * xprt;
};
/* Transport Wait Object private storage */
struct tw_priv {
pthread_cond_t cond;
struct rpc_msg *repl;
TXPRT_WAIT *next;
};
/* Dispatch service */
struct xt_svc {
u_long prog;
u_long vers;
void (*dispatch) (struct svc_req *r, SVCXPRT *x);
struct xt_svc *next;
};
/*
* A stream object private storage
*/
struct xt_priv {
pthread_mutex_t xdrlock;
pthread_mutex_t waitlock;
pthread_mutex_t readlock;
bool_t readlock_is_locked;
pthread_t readlock_by;
int svccnt;
pthread_mutex_t svccntlock;
pthread_attr_t pattr;
int sock;
struct rpc_err err;
XDR xdr;
SVCXPRT *svc;
struct xt_svc *callout;
TXPRT_WAIT *waitlist;
/* For select loop */
pthread_t select_thread;
bool_t stop;
caddr_t selectarg;
int (*select)(caddr_t arg);
void (*close)(caddr_t arg);
};
static int readtcp (caddr_t, char *, int);
static int writetcp (caddr_t, char *, int);
static void closetcp (caddr_t);
static int selecttcp (caddr_t);
static void xprt_thrd_run (TXPRT *xprt);
static SVCXPRT *xprt_get_svcxprt (TXPRT *xprt);
static struct xt_svc *svc_find (struct xt_priv *xtp, u_long prog, u_long vers,
bool_t *found, u_long *low, u_long *high);
/* threadsafe Waitlist handling */
static TXPRT_WAIT *find_wait (struct xt_priv *xtp, u_long x_id);
static void add_wait (struct xt_priv *xtp, TXPRT_WAIT *wait);
static void rem_wait (struct xt_priv *xtp, TXPRT_WAIT *wait);
/* We need to re-implement some of the low-level RPC functions */
static bool_t read_reply (XDR *xdrs, struct rpc_msg *rmsg);
static bool_t read_call (XDR *xdrs, struct rpc_msg *cmsg);
/* Ops vector for Threaded Transport */
static CLIENT * xprt_thrd_new_client (TXPRT *xprt, u_long proc, u_long vers);
static XDR * xprt_thrd_get_xdr (TXPRT *xprt);
static void xprt_thrd_rel_xdr (TXPRT *xprt);
static void xprt_thrd_wait_rep (TXPRT *xprt, struct rpc_msg *reply_msg,
XDR **xdrs, TXPRT_WAIT *wait,
struct timeval timeout);
static bool_t xprt_thrd_reg_call (TXPRT *xprt, u_long prog, u_long vers,
void (*dispatch) (struct svc_req *,
SVCXPRT *));
static caddr_t xprt_thrd_getsock (TXPRT *xprt);
static void xprt_thrd_destroy (TXPRT *xprt);
static void xprt_thrd_readlock (TXPRT *xprt);
static bool_t xprt_thrd_doread (TXPRT *xprt);
static const struct txprt_ops xprtthrd_ops =
{
xprt_thrd_new_client,
xprt_thrd_reg_call,
xprt_thrd_getsock,
xprt_thrd_destroy,
xprt_thrd_readlock,
xprt_thrd_doread,
xprt_thrd_get_xdr,
xprt_thrd_rel_xdr,
xprt_thrd_wait_rep
};
/*
* Create a baseline threaded transport
*/
TXPRT *
xprt_thrd_create (caddr_t sock,
int (*read)(caddr_t, char *, int),
int (*write)(caddr_t, char *, int),
void (*close)(caddr_t),
int (*select)(caddr_t),
u_int sendsz, u_int recvsz)
{
TXPRT *xprt;
struct xt_priv *xtp;
if ((read == NULL && write != NULL) ||
(read != NULL && write == NULL))
return NULL;
/* If read/write are supplied, must supply select and close */
if (read != NULL &&
(select == NULL || close == NULL))
return NULL;
xprt = (TXPRT *) malloc (sizeof (*xprt));
if (xprt == NULL) {
fprintf (stderr, ("xprt_thrd_create: out of memory\n"));
goto done;
}
xtp = (struct xt_priv *) malloc (sizeof (*xtp));
if (xtp == NULL) {
fprintf (stderr, ("xprt_thrd__create: out of memory\n"));
free ((char *) xprt);
xprt = (TXPRT *) NULL;
goto done;
}
memset (xprt, 0, sizeof (*xprt));
memset (xtp, 0, sizeof (*xtp));
/* Initialized Private context */
pthread_mutex_init (&(xtp->xdrlock), NULL);
pthread_mutex_init (&(xtp->waitlock), NULL);
pthread_mutex_init (&(xtp->readlock), NULL);
pthread_mutex_init (&(xtp->svccntlock), NULL);
pthread_attr_init (&(xtp->pattr));
pthread_attr_setdetachstate (&(xtp->pattr), PTHREAD_CREATE_DETACHED);
if (read == NULL) {
xtp->sock = (int) sock;
read = readtcp;
write = writetcp;
close = closetcp;
select = selecttcp;
sock = (caddr_t) xtp;
}
xdrrec_create (&(xtp->xdr), sendsz, recvsz, sock, read, write);
xtp->close = close;
xtp->select = select;
xtp->selectarg = sock;
/* Setup Transport Object */
xprt->txp_private = (caddr_t) xtp;
xprt->txp_ops = &xprtthrd_ops;
/* Start it running */
xprt_thrd_run (xprt);
done:
return xprt;
}
static CLIENT *
xprt_thrd_new_client (TXPRT *xprt, u_long proc, u_long vers)
{
return clntthrd_create (xprt, proc, vers);
}
static caddr_t
xprt_thrd_getsock (TXPRT *xprt)
{
struct xt_priv *xtp = (struct xt_priv *)xprt->txp_private;
return (xtp->selectarg);
}
static XDR *
xprt_thrd_get_xdr (TXPRT *xprt)
{
struct xt_priv *xtp = (struct xt_priv *)xprt->txp_private;
pthread_mutex_lock (&(xtp->xdrlock));
return &(xtp->xdr);
}
static void
xprt_thrd_readlock (TXPRT *xprt)
{
struct xt_priv *xtp = (struct xt_priv *)xprt->txp_private;
pthread_mutex_lock (&(xtp->readlock));
xtp->readlock_is_locked = TRUE;
xtp->readlock_by = pthread_self ();
}
static void
unlock_readlock (TXPRT *xprt)
{
struct xt_priv *xtp = (struct xt_priv *)xprt->txp_private;
xtp->readlock_is_locked = FALSE;
pthread_mutex_unlock (&(xtp->readlock));
}
static void
xprt_thrd_unlock_xdr (struct xt_priv *xtp)
{
pthread_mutex_unlock (&(xtp->xdrlock));
}
static void
xprt_thrd_rel_xdr (TXPRT *xprt)
{
struct xt_priv *xtp = (struct xt_priv *)xprt->txp_private;
/* Release the read lock if we're decoding */
if (xtp->xdr.x_op == XDR_DECODE && xtp->readlock_is_locked) {
unlock_readlock (xprt);
}
/* Then release the XDR lock */
xprt_thrd_unlock_xdr (xtp);
}
static void
xprt_thrd_wait_rep (TXPRT *xprt, struct rpc_msg *reply_msg,
XDR **xdrs, TXPRT_WAIT *wait, struct timeval timeout)
{
struct xt_priv *xtp = (struct xt_priv *)xprt->txp_private;
struct tw_priv *twp = (struct tw_priv *)wait->tw_priv;
/* Add wait to waitlist */
twp->repl = reply_msg;
add_wait (xtp, wait);
dfprintf ((stderr, "xprt_thrd_wait_rep: waiting for xid %lx\n", wait->tw_x_id));
/* wait for reply */
pthread_cond_wait (&twp->cond, &xtp->xdrlock);
/* Ok, we're awake, and we should have xdrlock held now */
/* Remove the wait from the waitlist */
rem_wait (xtp, wait);
/* And fill in the rest.. */
*xdrs = &(xtp->xdr);
(*xdrs)->x_op = XDR_DECODE;
return;
}
static bool_t
xprt_thrd_reg_call (TXPRT *xprt, u_long prog, u_long vers,
void (*dispatch) (struct svc_req *,
SVCXPRT *))
{
struct xt_priv *xtp = (struct xt_priv *)xprt->txp_private;
struct xt_svc *new;
if ((new = svc_find (xtp, prog, vers, NULL, NULL, NULL)) != NULL) {
/* re-registering???? Can't do that. */
return FALSE;
}
new = (struct xt_svc *) malloc (sizeof (*new));
if (new == NULL)
return FALSE;
new->prog = prog;
new->vers = vers;
new->dispatch = dispatch;
new->next = xtp->callout;
xtp->callout = new;
return TRUE;
}
static void
xprt_thrd_destroy (TXPRT *xprt)
{
struct xt_priv *xtp = (struct xt_priv *)xprt->txp_private;
struct xt_svc *t, *s;
TXPRT_WAIT *wait, *tempwait;
/*
* First, wait for the select thread to exit. If we're in the
* select thread (which can happen if the close() callback destroys
* the transport), then just continue -- we know we'll exit when
* control is returned.
*/
xtp->stop = TRUE;
if (xtp->select)
pthread_join (xtp->select_thread, NULL);
/* Clear out registered callouts */
for (s = xtp->callout; s != NULL; s = t) {
t = s->next;
free (s);
}
/* Wakeup waiting threads */
if (xtp->waitlist != NULL) {
/* Obtain the locks */
xprt_thrd_readlock (xprt);
xprt_thrd_get_xdr (xprt);
while ((wait = xtp->waitlist) != NULL) {
struct tw_priv *twp = (struct tw_priv *)wait->tw_priv;
XDR *xdr;
pthread_cond_signal (&twp->cond);
xprt_thrd_unlock_xdr (xtp);
/* We don't have to do anything here; we'll block waiting for readlock */
xprt_thrd_readlock (xprt);
xprt_thrd_get_xdr (xprt);
}
/*
* At this point we should have the locks held and all threads
* should be dead. So let's clear the locks and go home.
*/
xtp->xdr.x_op = XDR_DECODE;
xprt_thrd_rel_xdr (xprt);
}
/* Make sure all service calls have returned */
while (xtp->svccnt > 0);
/* Then clear my memory */
pthread_mutex_destroy (&(xtp->xdrlock));
pthread_mutex_destroy (&(xtp->waitlock));
pthread_mutex_destroy (&(xtp->readlock));
pthread_mutex_destroy (&(xtp->svccntlock));
pthread_attr_destroy (&(xtp->pattr));
XDR_DESTROY (&(xtp->xdr));
if (xtp->svc) svc_destroy(xtp->svc);
free (xtp);
free (xprt);
}
static void
xprt_thrd_destroy_wait (TXPRT_WAIT *wait)
{
struct tw_priv *twp = (struct tw_priv *)wait->tw_priv;
pthread_cond_destroy (&twp->cond);
free (twp);
free (wait);
}
TXPRT_WAIT *
xprt_thrd_new_wait (void)
{
TXPRT_WAIT *wait;
struct tw_priv *twp;
wait = (TXPRT_WAIT *)malloc (sizeof (*wait));
if (wait != NULL) {
twp = (struct tw_priv *) malloc (sizeof (*twp));
if (twp == NULL) {
free (wait);
return NULL;
}
memset (wait, 0, sizeof (*wait));
pthread_cond_init (&twp->cond, NULL);
twp->next = NULL;
wait->tw_priv = (caddr_t) twp;
wait->tw_destroy = xprt_thrd_destroy_wait;
}
return wait;
}
static SVCXPRT *
xprt_get_svcxprt (TXPRT *xprt)
{
struct xt_priv *xtp = (struct xt_priv *)xprt->txp_private;
if (xtp->svc == NULL) {
xtp->svc = svcthrd_create (xprt);
}
return xtp->svc;
}
/*
* Interface between xdr serializer and tcp connection.
* Behaves like the system calls, read & write, but keeps some error state
* around for the rpc level.
*/
static int
readtcp (char *ptr, char *buf, int len)
{
struct xt_priv *xt = (struct xt_priv *)ptr;
struct pollfd fd;
int milliseconds = 30000; /* XXX */
/* (ct->ct_wait.tv_sec * 1000) + (ct->ct_wait.tv_usec / 1000); */
if (len == 0)
return 0;
fd.fd = xt->sock;
fd.events = POLLIN;
while (TRUE)
{
switch (poll(&fd, 1, milliseconds))
{
case 0:
xt->err.re_status = RPC_TIMEDOUT;
return -1;
case -1:
if (errno == EINTR)
continue;
xt->err.re_status = RPC_CANTRECV;
xt->err.re_errno = errno;
return -1;
}
break;
}
switch (len = read (xt->sock, buf, len))
{
case 0:
/* premature eof */
xt->err.re_errno = ECONNRESET;
xt->err.re_status = RPC_CANTRECV;
len = -1; /* it's really an error */
break;
case -1:
xt->err.re_errno = errno;
xt->err.re_status = RPC_CANTRECV;
break;
}
return len;
}
static int
writetcp (caddr_t ptr, char *buf, int len)
{
int i, cnt;
struct xt_priv *xt = (struct xt_priv*)ptr;
for (cnt = len; cnt > 0; cnt -= i, buf += i)
{
if ((i = write (xt->sock, buf, cnt)) == -1)
{
xt->err.re_errno = errno;
xt->err.re_status = RPC_CANTSEND;
return -1;
}
}
return len;
}
static void
closetcp (caddr_t sock)
{
/* Do we close or not? */
}
static int
selecttcp (caddr_t sockp)
{
TXPRT *xprt = (TXPRT *)sockp;
struct xt_priv *xtp = (struct xt_priv *)xprt->txp_private;
int sock = xtp->sock;
struct timeval timeout;
fd_set fds;
timeout.tv_sec = 10;
timeout.tv_usec = 0;
FD_ZERO(&fds);
FD_SET(sock, &fds);
return select (sock+1, &fds, 0, 0, &timeout);
}
static struct xt_svc *
svc_find (struct xt_priv *xtp, u_long prog, u_long vers,
bool_t *found, u_long *low, u_long *high)
{
struct xt_svc *s = xtp->callout;
while (s != NULL) {
if (s->prog == prog) {
if (s->vers == vers) {
goto done;
}
if (found) *found = TRUE;
if (low && high) {
if (s->vers < *low)
*low = s->vers;
if (s->vers > *high)
*high = s->vers;
}
} /* s->prog != prog */
s = s->next;
}
done:
return s;
}
static TXPRT_WAIT *
find_wait (struct xt_priv *xtp, u_long x_id)
{
TXPRT_WAIT *wait;
struct tw_priv *twp;
pthread_mutex_lock (&xtp->waitlock);
wait = xtp->waitlist;
while (wait != NULL) {
if (wait->tw_x_id == x_id)
break;
twp = (struct tw_priv *)wait->tw_priv;
wait = twp->next;
}
pthread_mutex_unlock (&xtp->waitlock);
return wait;
}
static void
add_wait (struct xt_priv *xtp, TXPRT_WAIT *wait)
{
struct tw_priv *twp = (struct tw_priv *)wait->tw_priv;
pthread_mutex_lock (&xtp->waitlock);
twp->next = xtp->waitlist;
xtp->waitlist = wait;
pthread_mutex_unlock (&xtp->waitlock);
}
static void
rem_wait (struct xt_priv *xtp, TXPRT_WAIT *wait)
{
TXPRT_WAIT **oldwait;
pthread_mutex_lock (&xtp->waitlock);
oldwait = &(xtp->waitlist);
while (*oldwait != NULL) {
struct tw_priv *twp = (struct tw_priv *)(*oldwait)->tw_priv;
if (*oldwait == wait) {
*oldwait = twp->next;
break;
}
oldwait = &(twp->next);
}
pthread_mutex_unlock (&xtp->waitlock);
return;
}
bool_t
xprt_thrd_getargs_hook (XDR *x, void *v)
{
return FALSE;
}
static bool_t
read_call (XDR *xdrs, struct rpc_msg *cmsg)
{
if (
xdr_u_long (xdrs, &(cmsg->rm_call.cb_rpcvers)) &&
(cmsg->rm_call.cb_rpcvers == RPC_MSG_VERSION) &&
xdr_u_long (xdrs, &(cmsg->rm_call.cb_prog)) &&
xdr_u_long (xdrs, &(cmsg->rm_call.cb_vers)) &&
xdr_u_long (xdrs, &(cmsg->rm_call.cb_proc)) &&
xdr_opaque_auth (xdrs, &(cmsg->rm_call.cb_cred)))
return xdr_opaque_auth (xdrs, &(cmsg->rm_call.cb_verf));
return FALSE;
}
/* declarations from the SunRPC library */
extern bool_t xdr_accepted_reply (XDR*, void*);
extern bool_t xdr_rejected_reply (XDR*, void*);
static const struct xdr_discrim reply_dscrm[3] =
{
{(int) MSG_ACCEPTED, (xdrproc_t) xdr_accepted_reply},
{(int) MSG_DENIED, (xdrproc_t) xdr_rejected_reply},
{__dontcare__, NULL_xdrproc_t}};
static bool_t
read_reply (XDR *xdrs, struct rpc_msg *rmsg)
{
return xdr_union (xdrs, (enum_t *) & (rmsg->rm_reply.rp_stat),
(caddr_t) & (rmsg->rm_reply.ru), reply_dscrm,
NULL_xdrproc_t);
}
static void
svc_up (struct xt_priv *xtp)
{
pthread_mutex_lock (&(xtp->svccntlock));
xtp->svccnt++;
pthread_mutex_unlock (&(xtp->svccntlock));
}
static void
svc_down (struct xt_priv *xtp)
{
pthread_mutex_lock (&(xtp->svccntlock));
xtp->svccnt--;
pthread_mutex_unlock (&(xtp->svccntlock));
}
static void *
do_callsvc (void *rqst)
{
struct my_svc_req *mr = (struct my_svc_req *) rqst;
struct xt_priv *xtp = (struct xt_priv *)mr->xprt->txp_private;
/* Let the transport know we exist */
svc_up (xtp);
/* Usurp the readlock */
xtp->readlock_by = pthread_self ();
dfprintf ((stderr, "do_callsvc: xid=%lx\n", mr->m.rm_xid));
/* Call the dispatch */
(*(mr->dispatch))(&(mr->r), mr->r.rq_xprt);
dfprintf ((stderr, "do_callsvc return: xid=%lx\n", mr->m.rm_xid));
/* Now figure out if we need to unlock the readlock */
if (xtp->readlock_is_locked && pthread_equal (xtp->readlock_by,
pthread_self ()))
unlock_readlock (mr->xprt);
/* Clean up and exit */
free (mr);
svc_down (xtp);
return NULL;
}
/*
* The Magic reader. When this function is called we KNOW we have
* input but we don't know to whom the message is intended. It may be
* either a request or a response; if it is a request, we need to call
* out to the service. If it is a response, we need to wakeup the
* calling thread.
*
* NB: Assumes readlock is already held.
* NB: Assumes xdrlock is NOT held.
* Will release lock(s) as necessary on return
*/
static bool_t
xprt_thrd_doread (TXPRT *xprt)
{
struct xt_priv *xtp = (struct xt_priv *)xprt->txp_private;
struct rpc_msg msg;
XDR *xdr = xprt_thrd_get_xdr (xprt); /* Grab and Lock XDR! */
xdr->x_op = XDR_DECODE;
/* First, we need to make sure we're at the beginning of the record */
if (!xdrrec_skiprecord (xdr)) {
xprt_thrd_rel_xdr (xprt);
dfprintf ((stderr, "xprt_thrd_doread: skiprecord failed\n"));
return FALSE;
}
if (xdr_u_long (xdr, &(msg.rm_xid)) &&
xdr_enum (xdr, (enum_t *) &(msg.rm_direction))) {
/* Ok, we've got our x_id and direction... */
dfprintf ((stderr, "xprt_thrd_doread: %s for %lx\n",
(msg.rm_direction == CALL ? "call" :
(msg.rm_direction == REPLY ? "reply" : "(unk)")),
msg.rm_xid));
if (msg.rm_direction == CALL) {
struct my_svc_req *mr = (struct my_svc_req *) malloc (sizeof (*mr));
if (mr == NULL) {
xprt_thrd_rel_xdr (xprt);
fprintf (stderr, "xprt_thrd_doread: malloc failed\n");
return FALSE;
}
memset (mr, 0, sizeof (*mr));
mr->xprt = xprt;
mr->m.rm_xid = msg.rm_xid;
mr->m.rm_direction = msg.rm_direction;
mr->m.rm_call.cb_cred.oa_base = mr->cred;
mr->m.rm_call.cb_verf.oa_base = &(mr->cred[MAX_AUTH_BYTES]);
mr->r.rq_clntcred = &(mr->cred[2 * MAX_AUTH_BYTES]);
if (read_call (xdr, &(mr->m))) {
struct xt_svc *s;
enum auth_stat why;
bool_t found = FALSE;
u_long low = 0, high = 0;
mr->r.rq_prog = mr->m.rm_call.cb_prog;
mr->r.rq_vers = mr->m.rm_call.cb_vers;
mr->r.rq_proc = mr->m.rm_call.cb_proc;
mr->r.rq_cred = mr->m.rm_call.cb_cred;
mr->r.rq_xprt = xprt_get_svcxprt (xprt);
/* Setup for reply */
SVC_RECV (mr->r.rq_xprt, &(mr->m));
if ((why = _authenticate (&(mr->r), &(mr->m))) != AUTH_OK)
{
xprt_thrd_rel_xdr (xprt);
svcerr_auth (mr->r.rq_xprt, why);
goto call_done;
}
/* now match message with a registered service */
s = svc_find (xtp, mr->r.rq_prog, mr->r.rq_vers, &found, &low, &high);
if (s != NULL) {
pthread_t thr;
mr->dispatch = s->dispatch;
xprt_thrd_unlock_xdr (xtp);
dfprintf ((stderr, "xprt_thrd_doread: calling proc for xid %lx\n",
msg.rm_xid));
pthread_create (&thr, &(xtp->pattr), do_callsvc, mr);
mr = NULL;
goto call_done;
} else {
/* Or unlock XDR for to respond in error */
xprt_thrd_rel_xdr (xprt);
}
/*
* if we got here, the program or version
* is not served ...
*/
/* First we clear the locks */
if (found)
svcerr_progvers (mr->r.rq_xprt, low, high);
else
svcerr_noprog (mr->r.rq_xprt);
/* Fall through to ... */
call_done:
/* If we're here, then we had a "successful" call */
if (mr != NULL)
free (mr);
return TRUE;
} /* !read_call() */
if (mr != NULL)
free (mr);
dfprintf ((stderr, "xprt_thrd_doread: read_call failed, %lx\n",
msg.rm_xid));
} else if (msg.rm_direction == REPLY) {
/* Find the requestor thread.. */
TXPRT_WAIT *wait = find_wait(xtp, msg.rm_xid);
if (wait != NULL) {
struct tw_priv *twp = (struct tw_priv *) wait->tw_priv;
twp->repl->rm_xid = msg.rm_xid;
twp->repl->rm_direction = msg.rm_direction;
if (read_reply (xdr, twp->repl)) {
pthread_cond_signal (&twp->cond);
/* Now unlock the lock to let the other thread pick it up.
Note, keep the readlock locked! */
xprt_thrd_unlock_xdr (xtp);
return TRUE;
}
/* If we got here, then the reply didn't read correctly */
dfprintf ((stderr, "Reply didn't read correctly\n"));
}
/* If we got here, then either the reply didn't read or there is
no matching xid */
dfprintf ((stderr, "bad reply or no matching xid, %lx\n", msg.rm_xid));
}
/*
* If we got here, either the direction is bogus or the message
* didn't parse correctly
*/
dfprintf ((stderr, "bad message or bad direction\n"));
}
/* If we got here, then we've got a bad message */
xprt_thrd_rel_xdr (xprt);
dfprintf ((stderr, "xprt_thrd_doread: bad message\n"));
return FALSE;
}
static void *
do_select_loop (void *xprt)
{
struct xt_priv *xtp = (struct xt_priv *)((TXPRT *)xprt)->txp_private;
int (*select)(caddr_t) = xtp->select;
caddr_t arg = xtp->selectarg;
/* Get the readlock */
xprt_thrd_readlock (xprt);
while (1) {
if (xtp->stop) {
unlock_readlock (xprt);
return NULL;
}
/* now see if there is anything to read */
switch ((*select)(arg)) {
case -1:
/* error */
if (errno == EINTR)
break;
perror ("xprt_thrd: xprt_do_select_loop: select failed");
unlock_readlock (xprt);
return NULL;
case 0:
/* timeout */
continue;
default:
/* Go read the data */
if (!xprt_thrd_doread (xprt)) {
/* We Died */
xtp->stop = 1;
if (xtp->close)
xtp->close (xtp->selectarg);
return NULL;
}
/* Then wait until the reading is done */
xprt_thrd_readlock (xprt);
} /* select */
} /* while */
}
static void
xprt_thrd_run (TXPRT *xprt)
{
struct xt_priv *xtp = (struct xt_priv *)xprt->txp_private;
xtp->stop = FALSE;
/* start thread */
pthread_create (&(xtp->select_thread), NULL, do_select_loop, xprt);
}

191
src/engine/rpc/xprt_thrd.h Normal file
View File

@ -0,0 +1,191 @@
/*
* xprt_thrd.h
*
* Threaded TCP Transport scheme for RPC; allow a client and a service
* to share a single (TCP) stream. A single reader/writer thread
* exists to handle to I/O. Writers obtain a write-lock before
* writing to the stream, and 'readers' setup a callback with the I/O
* thread which wakes them up when a particular request (or reply) is
* received.
*
* Created by: Derek Atkins <warlord@MIT.EDU>
*
*/
#ifndef RPC_XPRT_THRD_H
#define RPC_XPRT_THRD_H
#include <rpc/types.h>
#include <rpc/xdr.h>
#include <rpc/clnt.h>
#include <rpc/svc.h>
#include <rpc/rpc_msg.h>
typedef struct TXPRT_WAIT TXPRT_WAIT;
typedef struct TXPRT TXPRT;
struct TXPRT {
const struct txprt_ops {
/*
* PUBLIC interfaces
*/
CLIENT * (*txp_new_clnt) (TXPRT *xprt, u_long prog, u_long vers);
/* build and return a new CLIENT object on this transport */
bool_t (*txp_reg_call) (TXPRT *xprt, u_long prog, u_long vers,
void (*dispatch) (struct svc_req *,
SVCXPRT *));
/* register a callout to a service/dispatch */
caddr_t (*txp_get_sock) (TXPRT *xprt);
/* Return the socket pointer for a transport */
void (*txp_destroy) (TXPRT *xprt);
/* destroy a threaded transport (does NOT destroy CLIENTs nor does
it call close() */
/*
* Semi-Private interfaces
* (for use in writing your own select loop)
*/
void (*txp_readlock) (TXPRT *xptr);
/* Obtain the read lock on the underlying transport */
bool_t (*txp_doread) (TXPRT *xptr);
/* When the read-lock is held and data exists on this transport, read it */
/*
* PRIVATE interfaces
*/
XDR * (*txp_get_xdr) (TXPRT *xprt);
/* get and lock the XDR object (blocks until lock can be obtained) */
void (*txp_rel_xdr) (TXPRT *xprt);
/* release/unlock the XDR object */
void (*txp_wait_rep) (TXPRT *xprt, struct rpc_msg *reply_msg,
XDR **xdrs, TXPRT_WAIT *wait,
struct timeval timeout);
/* wait for a reply to the call, return with reply_msg filled
* and xdrs filled and locked */
} *txp_ops;
caddr_t txp_private; /* Private Stuff */
};
/*
* operations on a Threaded Transport handle:
*
* TXPRT *xprt
* u_long prog, vers
* void (*)(struct svc_req *, SVCSPRT *) disp
*/
/*
* PUBLIC Interfaces
*/
#define TXPRT_NEW_CLIENT(xprt,prog,vers) \
(*(xprt)->txp_ops->txp_new_clnt)((xprt),(prog),(vers))
#define txprt_new_client(xprt,prog,vers) \
(*(xprt)->txp_ops->txp_new_clnt)((xprt),(prog),(vers))
#define TXPRT_REG_CALLOUT(xprt,prog,vers,disp) \
(*(xprt)->txp_ops->txp_reg_call)((xprt),(prog),(vers),(disp))
#define txprt_reg_callout(xprt,prog,vers,disp) \
(*(xprt)->txp_ops->txp_reg_call)((xprt),(prog),(vers),(disp))
#define TXPRT_DESTROY(xprt) (*(xprt)->txp_ops->txp_destroy)(xprt)
#define txprt_destroy(xprt) (*(xprt)->txp_ops->txp_destroy)(xprt)
#define TXPRT_GETSOCK(xprt) (*(xprt)->txp_ops->txp_get_sock)(xprt)
#define txprt_getsock(xprt) (*(xprt)->txp_ops->txp_get_sock)(xprt)
/*
* Semi-Private interfaces
* (for use in writing your own select loop)
*
* NOTE: you must lock readlock and then call doread with the lock held;
* doread will unlock the readlock when it is done.
*/
#define TXPRT_READLOCK(xprt) (*(xprt)->txp_ops->txp_readlock)(xprt)
#define txprt_readlock(xprt) (*(xprt)->txp_ops->txp_readlock)(xprt)
#define TXPRT_DOREAD(xprt) (*(xprt)->txp_ops->txp_doread)(xprt)
#define txprt_doread(xprt) (*(xprt)->txp_ops->txp_doread)(xprt)
/*
* Transport Creation/Destruction
*/
extern TXPRT * xprt_thrd_create (caddr_t sock,
int (*read)(caddr_t, char *, int),
int (*write)(caddr_t, char *, int),
void (*close)(caddr_t),
int (*select)(caddr_t),
u_int sendsz, u_int recvsz);
/*
* This is an xdrproc_t function that, when passed into a threaded
* SVCXPRT will obtain the TXPRT * from the service. It should be
* used by service routines if they want to obtain the TXPRT * to
* e.g. build a CLIENT for a callback from a request. It should be
* used like this:
*
* SVCXPRT *svc
* TXPRT *xprt
* bool_t res
*
* res = svc_getargs(svc, (xdrproc_t) xprt_thrd_getargs_hook,
* (caddr_t) &xprt);
*
* If res == TRUE, then xprt is a valid pointer. If res == FALSE,
* then svc is not a threaded transport service (and xprt is invalid)
*/
extern bool_t xprt_thrd_getargs_hook (XDR *, void *);
/*
* PRIVATE Interfaces
*/
struct TXPRT_WAIT {
u_long tw_x_id;
void (*tw_destroy)(TXPRT_WAIT *);
caddr_t tw_priv;
};
/*
* operations on a Threaded Transport handle:
*
* TXPRT *xprt
* XDR **xdrp
* struct rpc_msg *rmsg
* TXPRT_WAIT *wait (with tw_x_id filled in with the request ID)
*/
/* Returns and locks XDR */
#define TXPRT_GET_XDR(xprt) (*(xprt)->txp_ops->txp_get_xdr)(xprt)
#define txprt_get_xdr(xprt) (*(xprt)->txp_ops->txp_get_xdr)(xprt)
/* Unlocks XDR (and potentially the ReadLock if x_op == XDR_DECODE */
#define TXPRT_REL_XDR(xprt) (*(xprt)->txp_ops->txp_rel_xdr)(xprt)
#define txprt_rel_xdr(xprt) (*(xprt)->txp_ops->txp_rel_xdr)(xprt)
/* Wait for a reply -- blocks until we get a response for wait->tw_x_id */
#define TXPRT_WAIT_REPLY(xprt,rmsg,xdrp,wait,time) \
(*(xprt)->txp_ops->txp_wait_rep)((xprt),(rmsg),(xdrp),(wait),(time))
#define txprt_wait_reply(xprt,rmsg,xdrp,wait,time) \
(*(xprt)->txp_ops->txp_wait_rep)((xprt),(rmsg),(xdrp),(wait),(time))
/* create a wait object (for use in the client) */
extern TXPRT_WAIT * xprt_thrd_new_wait (void);
/* Destroy a wait object */
#define XPRT_DESTROY_WAIT(wait) (*(wait)->tw_destroy)(wait)
#define xprt_destroy_wait(wait) (*(wait)->tw_destroy)(wait)
#endif /* RPC_XPRT_THRD_H */

View File

@ -211,7 +211,14 @@
'boolean
gnc:load-system-config-if-needed
#f
(N_ "Load the system configuation"))))
(N_ "Load the system configuation"))
(list "rpc-server"
'boolean
(lambda (val)
(if val (gnc:run-rpc-server)))
#f
(N_ "Run the RPC Server"))))
(define (gnc:cmd-line-get-boolean-arg args)
;; --arg means #t