1
0
Fork 0
mirror of git://git.code.sf.net/p/cdesktopenv/code synced 2025-03-09 15:50:02 +00:00
cde/cde/lib/csa/agent.c

814 lines
20 KiB
C

/*
* CDE - Common Desktop Environment
*
* Copyright (c) 1993-2012, The Open Group. All rights reserved.
*
* These libraries and programs are free software; you can
* redistribute them and/or modify them under the terms of the GNU
* Lesser General Public License as published by the Free Software
* Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* These libraries and programs are distributed in the hope that
* they will be useful, but WITHOUT ANY WARRANTY; without even the
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU Lesser General Public License for more
* details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with these librararies and programs; if not, write
* to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
* Floor, Boston, MA 02110-1301 USA
*/
/* $TOG: agent.c /main/2 1998/03/16 14:42:16 mgreess $ */
/*
* (c) Copyright 1993, 1994 Hewlett-Packard Company
* (c) Copyright 1993, 1994 International Business Machines Corp.
* (c) Copyright 1993, 1994 Novell, Inc.
* (c) Copyright 1993, 1994 Sun Microsystems, Inc.
*/
#include <EUSCompat.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <rpc/rpc.h>
#if defined(linux)
# include <sys/poll.h>
#else
# include <poll.h>
#endif
#if defined(SunOS) || defined(USL) || defined(__uxp__)
#include <netconfig.h>
#include <netdir.h>
#else
#include <sys/signal.h>
#include <sys/socket.h>
#include <sys/errno.h>
#endif /* SunOS || USL || __uxp__ */
#include <X11/Intrinsic.h>
#include "agent.h"
#include "cmcb.h"
#include "entry.h"
#include "debug.h"
#include "iso8601.h"
#include "free.h"
#include "misc.h"
/*
* callback info
* - contain update information from backend
*/
typedef struct cb_info {
int vers;
char *cal;
char *user;
int reason;
void *data;
struct cb_info *next;
} _CallbackInfo;
static _CallbackInfo *cb_head = NULL, *cb_tail = NULL;
u_long _DtCm_transient = 0;
static u_long prognum = 0x40000000;
static int mapped = 0;
/******************************************************************************
* forward declaration of static functions used within the file
******************************************************************************/
#if defined(SunOS) || defined(USL) || defined(__uxp__)
static u_long gettransient (u_long version);
#else
static u_long gettransient (int proto, u_long vers, int *sockp);
#endif
static void _DtCm_handle_callback();
static CSA_return_code _ConvertCallbackData(cmcb_update_callback_args *args,
_CallbackInfo **cbi);
static CSA_return_code _CopyAttributeNames(uint fnum, char **fnames,
CSA_uint32 *tnum, char ***tnames);
static boolean_t _ProcessV1Callback(_CallbackInfo *ptr);
static boolean_t _ProcessV2Callback(_CallbackInfo *ptr);
static void _FreeCallbackInfo(_CallbackInfo *ptr);
/*****************************************************************************
* extern functions used in the library
*****************************************************************************/
/*
* Register rpc service for server callback
*/
extern void
_DtCm_init_agent()
{
int s = RPC_ANYSOCK;
XtInputId id;
#if defined(SunOS) && SunOS < 55 || defined(USL) || defined(__uxp__)
extern boolean_t rpc_reg(const u_long, const u_long, const u_long,
const char *(*)(), const xdrproc_t, const xdrproc_t,
const char *);
#endif
#if defined(SunOS) || defined(USL)|| defined(__uxp__)
extern void (*sigset(int, void (*)(int)))(int);
#else
extern void (*sigset())();
#endif
/* locking candidate for MT-safe purpose */
if (mapped == 1)
return;
DP(("libdtcm: _DtCm_init_agent\n"));
#if defined(SunOS) || defined(USL) || defined(__uxp__)
(void)rpcb_unset(_DtCm_transient, AGENTVERS, NULL);
if ((_DtCm_transient = gettransient((u_long)1)) == 0) {
_DtCm_print_errmsg("Cannot get transient program number\n");
_DtCm_print_errmsg("Callback cannot be enabled.\n");
return;
}
/* register v1 callback */
if (rpc_reg(_DtCm_transient, AGENTVERS, update_callback,
(const char *(*)())_DtCm_update_callback_1, _DtCm_xdr_Table_Res_4,
_DtCm_xdr_Update_Status, "udp") == -1) {
_DtCm_print_errmsg("Cannot register v1 callback handler\n");
_DtCm_print_errmsg("Callback cannot be enabled.\n");
}
/* register v2 callback */
if (rpc_reg(_DtCm_transient, AGENTVERS_2, CMCB_UPDATE_CALLBACK,
(const char *(*)())cmcb_update_callback_2_svc,
xdr_cmcb_update_callback_args, xdr_void, "udp") == -1) {
_DtCm_print_errmsg("Cannot register v2 callback handler\n");
_DtCm_print_errmsg("Callback cannot be enabled.\n");
}
#else
(void)pmap_unset(_DtCm_transient, AGENTVERS);
if ((_DtCm_transient = gettransient(IPPROTO_UDP,(u_long)1, &s)) == 0) {
_DtCm_print_errmsg("Cannot get transient program number\n");
_DtCm_print_errmsg("Callback cannot be enabled.\n");
return;
}
if (registerrpc(_DtCm_transient, AGENTVERS, update_callback,
(char *(*)())_DtCm_update_callback_1, _DtCm_xdr_Table_Res_4,
_DtCm_xdr_Update_Status) == -1) {
_DtCm_print_errmsg("Cannot register v1 callback handler\n");
_DtCm_print_errmsg("Callback cannot be enabled.\n");
}
if (registerrpc(_DtCm_transient, AGENTVERS_2, CMCB_UPDATE_CALLBACK,
(char *(*)())cmcb_update_callback_2_svc,
xdr_cmcb_update_callback_args, xdr_void) == -1) {
_DtCm_print_errmsg("Cannot register v2 callback handler\n");
_DtCm_print_errmsg("Callback cannot be enabled.\n");
}
#endif /* SunOS || USL || __uxp__ */
/* locking candidate for MT-safe purpose */
mapped = 1;
}
/*
* Unregister with the rpc service.
*/
extern void
_DtCm_destroy_agent()
{
if (mapped == 0)
return;
DP(("libdtcm: _DtCm_destroy_agent\n"));
#if defined(SunOS) || defined(USL) || defined(__uxp__)
(void) rpcb_unset(_DtCm_transient, AGENTVERS, NULL);
(void) rpcb_unset(_DtCm_transient, AGENTVERS_2, NULL);
#else
(void) pmap_unset(_DtCm_transient, AGENTVERS);
(void) pmap_unset(_DtCm_transient, AGENTVERS_2);
#endif /* SunOS || USL || __uxp__ */
/* locking candidate for MT-safe purpose */
mapped = 0;
}
extern void
_DtCm_process_updates()
{
int i, j, nfd;
fd_set rpc_bits;
fd_mask fmask, *inbits;
struct pollfd pfd[FD_SETSIZE];
struct pollfd *p;
int last;
int do_rpc;
while (B_TRUE) {
rpc_bits = svc_fdset;
/* convert to pollfd structure */
#if defined(linux)
inbits = rpc_bits.__fds_bits;
#else
inbits = rpc_bits.fds_bits;
#endif
p = pfd;
for (i = 0; i < FD_SETSIZE; i += NFDBITS) {
fmask = *inbits;
for (j = 0; fmask != 0 ; j++, fmask >>= 1) {
if (fmask & 0x1) {
p->fd = i + j;
if (p->fd >= FD_SETSIZE)
break;
p->events = POLLIN;
p++;
}
}
inbits++;
}
/* poll and return right away */
i = p - pfd;
nfd = poll(pfd, i, 0);
if (nfd <= 0)
/* done */
return;
/* if set, handle rpc calls */
/* convert back to fd_set structure */
last = -1;
do_rpc = 0;
FD_ZERO(&rpc_bits);
for (p = pfd; i-- > 0; p++) {
j = p->fd / NFDBITS;
if (j != last) {
#if defined(linux)
inbits = &rpc_bits.__fds_bits[j];
#else
inbits = &rpc_bits.fds_bits[j];
#endif
last = j;
}
if (p->revents & POLLIN) {
*inbits |= (1 << (p->fd % NFDBITS));
do_rpc = 1;
}
}
if (do_rpc)
svc_getreqset(&rpc_bits);
}
}
/*
* The server calls this routine when an update event occurs;
* It's job is to notify CM asynchronously that an
* update has occurred. It has to do it this
* way (i.e. raise a signal) because the client
* can't make an rpc call until this procedure call has
* returned to the server.
*/
Update_Status *
_DtCm_update_callback_1(Table_Res_4 *t, void *dummy)
{
static Update_Status status = update_succeeded;
_CallbackInfo *cbi;
DP(("agent.c: _DtCm_update_callback_1()\n"));
/*
* no point to save data for version 4 and before
* since the info from old backends does not contain
* calendar info
* so we just invoke all registered callback with no data
*/
if (cbi = (_CallbackInfo *)calloc(1, sizeof(_CallbackInfo))) {
cbi->vers = AGENTVERS;
if (cb_tail == NULL)
cb_head = cbi;
else
cb_tail->next = cbi;
cb_tail = cbi;
}
/* handle callback from backend */
_DtCm_handle_callback();
return (&status);
}
/*
* Handler for v2 callback protocol
*/
void *
cmcb_update_callback_2_svc(cmcb_update_callback_args *args, struct svc_req *d)
{
_CallbackInfo *cbi;
DP(("agent.c: cmcb_update_callback_2_svc()\n"));
if (args == NULL)
return (NULL);
if (_ConvertCallbackData(args, &cbi) == CSA_SUCCESS) {
cbi->vers = AGENTVERS_2;
if (cb_tail == NULL)
cb_head = cbi;
else
cb_tail->next = cbi;
cb_tail = cbi;
/* handle callback from backend */
_DtCm_handle_callback();
}
return (NULL);
}
/******************************************************************************
* static functions used within in the file
******************************************************************************/
/*
* get transient program number for callbacks.
*/
#if defined(SunOS) || defined(USL) || defined(__uxp__)
static u_long
gettransient (u_long version)
{
int stat;
struct nd_hostserv host = {HOST_SELF, "rpcbind"};
struct nd_addrlist *addrp;
struct netbuf *addr;
struct netconfig *netconf;
netconf = getnetconfigent("udp");
if (!netconf) {
DP(("(gettransient) getnetconfigent(udp) failed\n"));
freenetconfigent(netconf);
return 0;
}
stat = netdir_getbyname(netconf, &host, &addrp);
if (stat) {
DP(("(gettransient) netdir_getbyname failed\n"));
netdir_free(addrp, ND_ADDRLIST);
freenetconfigent(netconf);
return 0;
}
if (addrp->n_cnt < 1) {
DP(("(gettransient) netdir_getbyname - zero addresses\n"));
netdir_free(addrp, ND_ADDRLIST);
freenetconfigent(netconf);
return 0;
}
addr = addrp->n_addrs;
while (!rpcb_set(prognum++, version, netconf, addr))
continue;
netdir_free(addrp, ND_ADDRLIST);
freenetconfigent(netconf);
prognum--;
return prognum;
}
#else /* SunOS || USL || __uxp__ */
static u_long
gettransient (int proto, u_long vers, int *sockp)
{
int s, len, socktype;
struct sockaddr_in addr;
switch (proto) {
case IPPROTO_UDP:
socktype = SOCK_DGRAM;
break;
case IPPROTO_TCP:
socktype = SOCK_STREAM;
break;
default:
DP(("unknown protocol type\n"));
return 0;
}
if (*sockp == RPC_ANYSOCK) {
if ((s = socket(AF_INET, socktype, 0)) < 0) {
perror("socket");
return 0;
}
*sockp = s;
} else
s = *sockp;
addr.sin_addr.s_addr = 0;
addr.sin_family = AF_INET;
addr.sin_port = 0;
len = sizeof(addr);
if (bind(s, (struct sockaddr *)&addr, len) != 0) {
perror("bind");
return 0;
}
if (getsockname(s, (struct sockaddr *)&addr, &len) < 0) {
perror("getsockname");
return 0;
}
while (!pmap_set(prognum++, vers, proto, ntohs(addr.sin_port)))
continue;
return (prognum-1);
}
#endif /* not SunOS || USL || __uxp__ */
static void
_DtCm_handle_callback()
{
_CallbackInfo *ptr, *prev;
Calendar *cal;
_DtCmCallbackEntry *cb;
boolean_t keep = B_FALSE;
DP(("agent.c: _DtCm_handle_callback()\n"));
for (ptr = cb_head, prev = NULL; ptr != NULL; ) {
/* we only handle version 1 and version 2 */
if (ptr->vers == AGENTVERS)
keep = _ProcessV1Callback(ptr);
else
keep = _ProcessV2Callback(ptr);
if (!keep) {
if (prev == NULL)
cb_head = ptr->next;
else
prev->next = ptr->next;
_FreeCallbackInfo(ptr);
} else {
prev = ptr;
}
ptr = ptr->next;
}
cb_tail = prev;
}
static CSA_return_code
_ConvertCallbackData(cmcb_update_callback_args *args, _CallbackInfo **cbi)
{
_CallbackInfo *ncbi;
CSA_calendar_user *user;
CSA_logon_callback_data *ldata;
CSA_calendar_deleted_callback_data *rdata;
CSA_calendar_attr_update_callback_data *cdata;
CSA_add_entry_callback_data *adata;
CSA_delete_entry_callback_data *ddata;
CSA_update_entry_callback_data *udata;
char timebuf[BUFSIZ];
if ((ncbi = (_CallbackInfo *)calloc(1, sizeof(_CallbackInfo))) == NULL)
return (CSA_E_INSUFFICIENT_MEMORY);
if (args->calendar && (ncbi->cal = strdup(args->calendar)) == NULL) {
free(ncbi);
return (CSA_E_INSUFFICIENT_MEMORY);
}
if (args->user && (ncbi->user = strdup(args->user)) == NULL) {
_FreeCallbackInfo(ncbi);
return (CSA_E_INSUFFICIENT_MEMORY);
}
if ((user = (CSA_calendar_user *)calloc(1, sizeof(CSA_calendar_user)))
== NULL) {
_FreeCallbackInfo(ncbi);
return (CSA_E_INSUFFICIENT_MEMORY);
} else
user->user_name = ncbi->user;
ncbi->reason = args->data.reason;
switch (ncbi->reason) {
case CSA_CB_CALENDAR_LOGON:
if ((ldata = (CSA_logon_callback_data *)calloc(1,
sizeof(CSA_logon_callback_data))) == NULL) {
free(user);
_FreeCallbackInfo(ncbi);
return (CSA_E_INSUFFICIENT_MEMORY);
}
ldata->user = user;
ncbi->data = (void *)ldata;
break;
case CSA_CB_CALENDAR_DELETED:
if ((rdata = (CSA_calendar_deleted_callback_data *)calloc(1,
sizeof(CSA_calendar_deleted_callback_data))) == NULL) {
free(user);
_FreeCallbackInfo(ncbi);
return (CSA_E_INSUFFICIENT_MEMORY);
}
rdata->user = user;
ncbi->data = (void *)rdata;
break;
case CSA_CB_CALENDAR_ATTRIBUTE_UPDATED:
if ((cdata = (CSA_calendar_attr_update_callback_data *)
calloc(1, sizeof(
CSA_calendar_attr_update_callback_data))) == NULL) {
free(user);
_FreeCallbackInfo(ncbi);
return (CSA_E_INSUFFICIENT_MEMORY);
}
cdata->user = user;
ncbi->data = (void *)cdata;
if (_CopyAttributeNames(args->data.data.cdata->num_names,
args->data.data.cdata->names, &cdata->number_attributes,
&cdata->attribute_names)) {
_FreeCallbackInfo(ncbi);
return (CSA_E_INSUFFICIENT_MEMORY);
}
break;
case CSA_CB_ENTRY_ADDED:
if ((adata = (CSA_add_entry_callback_data *)calloc(1,
sizeof(CSA_add_entry_callback_data))) == NULL) {
free(user);
_FreeCallbackInfo(ncbi);
return (CSA_E_INSUFFICIENT_MEMORY);
}
adata->user = user;
ncbi->data = (void *)adata;
if (args->data.data.adata->id && (adata->added_entry_id.data =
(unsigned char *)strdup(args->data.data.adata->id))
== NULL) {
_FreeCallbackInfo(ncbi);
return (CSA_E_INSUFFICIENT_MEMORY);
} else
adata->added_entry_id.size =
strlen((char *)adata->added_entry_id.data);
break;
case CSA_CB_ENTRY_DELETED:
if ((ddata = (CSA_delete_entry_callback_data *)calloc(1,
sizeof(CSA_delete_entry_callback_data))) == NULL) {
free(user);
_FreeCallbackInfo(ncbi);
return (CSA_E_INSUFFICIENT_MEMORY);
}
ddata->user = user;
ncbi->data = (void *)ddata;
if (args->data.data.ddata->id && (ddata->deleted_entry_id.data =
(unsigned char *)strdup(args->data.data.ddata->id))
== NULL) {
_FreeCallbackInfo(ncbi);
return (CSA_E_INSUFFICIENT_MEMORY);
} else
ddata->deleted_entry_id.size =
strlen((char *)ddata->deleted_entry_id.data);
_csa_tick_to_iso8601(args->data.data.ddata->time, timebuf);
if ((ddata->date_and_time = strdup(timebuf)) == NULL) {
_FreeCallbackInfo(ncbi);
return (CSA_E_INSUFFICIENT_MEMORY);
}
ddata->scope = args->data.data.ddata->scope;
break;
case CSA_CB_ENTRY_UPDATED:
if ((udata = (CSA_update_entry_callback_data *)calloc(1,
sizeof(CSA_update_entry_callback_data))) == NULL) {
free(user);
_FreeCallbackInfo(ncbi);
return (CSA_E_INSUFFICIENT_MEMORY);
}
udata->user = user;
ncbi->data = (void *)udata;
if (args->data.data.udata->newid && (udata->new_entry_id.data =
(unsigned char *)strdup(args->data.data.udata->newid))
== NULL) {
_FreeCallbackInfo(ncbi);
return (CSA_E_INSUFFICIENT_MEMORY);
} else
udata->new_entry_id.size =
strlen((char *)udata->new_entry_id.data);
if (args->data.data.udata->oldid && (udata->old_entry_id.data =
(unsigned char *)strdup(args->data.data.udata->oldid))
== NULL) {
_FreeCallbackInfo(ncbi);
return (CSA_E_INSUFFICIENT_MEMORY);
} else
udata->old_entry_id.size =
strlen((char *)udata->old_entry_id.data);
_csa_tick_to_iso8601(args->data.data.udata->time, timebuf);
if ((udata->date_and_time = strdup(timebuf)) == NULL) {
_FreeCallbackInfo(ncbi);
return (CSA_E_INSUFFICIENT_MEMORY);
}
udata->scope = args->data.data.udata->scope;
break;
}
*cbi = ncbi;
return (CSA_SUCCESS);
}
static CSA_return_code
_CopyAttributeNames(uint fnum, char **fnames, CSA_uint32 *tnum, char ***tnames)
{
int i;
char **nnames;
if (fnum == 0) {
*tnum = 0;
*tnames = NULL;
return (CSA_SUCCESS);
}
if ((nnames = calloc(1, sizeof(char *) * fnum)) == NULL)
return (CSA_E_INSUFFICIENT_MEMORY);
for (i = 0; i < fnum; i++) {
if ((nnames[i] = strdup(fnames[i])) == NULL)
break;
}
if (i == fnum) {
*tnum = i;
*tnames = nnames;
return (CSA_SUCCESS);
} else {
_DtCm_free_character_pointers(i, nnames);
free(nnames);
return (CSA_E_INSUFFICIENT_MEMORY);
}
}
/*
* pre callback protocol V2, there is no distinction
* between different reasons, and no data passed. So
* there's only one possible thing to do, and that's
* run all the callbacks.
*/
static boolean_t
_ProcessV1Callback(_CallbackInfo *ptr)
{
Calendar *cal;
_DtCmCallbackEntry *cb;
boolean_t keep = B_FALSE;
for (cal = _DtCm_active_cal_list; cal != NULL; cal = cal->next) {
if (cal->rpc_version >= _DtCM_FIRST_EXTENSIBLE_SERVER_VERSION)
continue;
if (cal->do_reasons &
(CSA_CB_ENTRY_ADDED | CSA_CB_ENTRY_DELETED |
CSA_CB_ENTRY_UPDATED))
{
/* only do dumb processing if it was a V4 server */
for (cb = cal->cb_list; cb != NULL; cb = cb->next) {
if (cal->do_reasons & cb->reason) {
cb->handler((CSA_session_handle)cal,
cal->do_reasons & cb->reason,
(CSA_buffer) NULL,
cb->client_data,
(CSA_extension*) NULL);
}
}
} else if (cal->all_reasons & (CSA_CB_ENTRY_ADDED |
CSA_CB_ENTRY_DELETED | CSA_CB_ENTRY_UPDATED))
keep = B_TRUE;
}
return (keep);
}
static boolean_t
_ProcessV2Callback(_CallbackInfo *ptr)
{
Calendar *cal;
_DtCmCallbackEntry *cb;
boolean_t keep = B_FALSE;
for (cal = _DtCm_active_cal_list; cal != NULL; cal = cal->next) {
if (cal->rpc_version < _DtCM_FIRST_EXTENSIBLE_SERVER_VERSION ||
strcmp(ptr->cal, cal->name))
continue;
if (cal->do_reasons & ptr->reason) {
/* only do dumb processing if it was a V4 server */
for (cb = cal->cb_list; cb != NULL; cb = cb->next) {
if (ptr->reason & cb->reason) {
cb->handler((CSA_session_handle)cal,
ptr->reason,
(CSA_buffer)ptr->data,
cb->client_data,
(CSA_extension*) NULL);
}
}
} else if (cal->all_reasons & ptr->reason)
keep = B_TRUE;
}
return (keep);
}
static void
_FreeCallbackInfo(_CallbackInfo *ptr)
{
CSA_logon_callback_data *ldata;
CSA_calendar_deleted_callback_data *rdata;
CSA_calendar_attr_update_callback_data *cdata;
CSA_add_entry_callback_data *adata;
CSA_delete_entry_callback_data *ddata;
CSA_update_entry_callback_data *udata;
if (ptr) {
if (ptr->cal) free(ptr->cal);
if (ptr->user) free(ptr->user);
if (ptr->data) switch (ptr->reason) {
case CSA_CB_CALENDAR_LOGON:
ldata = ptr->data;
if (ldata->user) free(ldata->user);
free(ldata);
break;
case CSA_CB_CALENDAR_DELETED:
rdata = ptr->data;
if (rdata->user) free(rdata->user);
free(rdata);
break;
case CSA_CB_CALENDAR_ATTRIBUTE_UPDATED:
cdata = (CSA_calendar_attr_update_callback_data *)
ptr->data;
if (cdata->user) free(cdata->user);
if (cdata->number_attributes > 0)
_DtCm_free_character_pointers(
cdata->number_attributes,
cdata->attribute_names);
free(cdata);
break;
case CSA_CB_ENTRY_ADDED:
adata = (CSA_add_entry_callback_data *)ptr->data;
if (adata->user) free(adata->user);
if (adata->added_entry_id.data)
free(adata->added_entry_id.data);
free(adata);
break;
case CSA_CB_ENTRY_DELETED:
ddata = (CSA_delete_entry_callback_data *)ptr->data;
if (ddata->date_and_time) free(ddata->date_and_time);
if (ddata->user) free(ddata->user);
if (ddata->deleted_entry_id.data)
free(ddata->deleted_entry_id.data);
free(ddata);
break;
case CSA_CB_ENTRY_UPDATED:
udata = (CSA_update_entry_callback_data *)ptr->data;
if (udata->user) free(udata->user);
if (udata->date_and_time) free(udata->date_and_time);
if (udata->old_entry_id.data)
free(udata->old_entry_id.data);
if (udata->new_entry_id.data)
free(udata->new_entry_id.data);
free(udata);
}
free(ptr);
}
}