cdesktopenv/cde/programs/dtcm/server/callback.c

683 lines
15 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 libraries and programs; if not, write
* to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
* Floor, Boston, MA 02110-1301 USA
*/
/* $XConsortium: callback.c /main/5 1996/10/03 10:40:51 drk $ */
/*
* (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 <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <sys/time.h>
#include <rpc/rpc.h>
#include "agent.h"
#include "cmcb.h"
#include "callback.h"
#include "appt4.h"
#include "utility.h"
#include "lutil.h"
extern int debug;
extern char *pgname;
/*
* forward declaration of static functions used within the file
*/
static _DtCmsRegistrationInfo * _DtCmsDoCallback(
_DtCmsRegistrationInfo *rlist,
uint type,
char *source,
int pid,
int version,
void *args);
/*****************************************************************************
* extern functions
*****************************************************************************/
extern _DtCmsRegistrationInfo *
_DtCmsMakeRegistrationInfo(
char *client,
int types,
u_long prognum,
u_long versnum,
u_long procnum,
int pid)
{
_DtCmsRegistrationInfo *rinfo;
if ((rinfo = (_DtCmsRegistrationInfo *)calloc(1,
sizeof(_DtCmsRegistrationInfo))) == NULL)
return (NULL);
if ((rinfo->client = strdup(client)) == NULL) {
free(rinfo);
return (NULL);
}
rinfo->types = types;
rinfo->prognum = prognum;
rinfo->versnum = versnum;
rinfo->procnum = procnum;
rinfo->pid = pid;
rinfo->next = NULL;
return(rinfo);
}
extern void
_DtCmsFreeRegistrationInfo(_DtCmsRegistrationInfo *rinfo)
{
if (rinfo==NULL) return;
if (rinfo->client != NULL)
free(rinfo->client);
free(rinfo);
}
/*
* this routine is for v4, so type is not checked
*/
extern _DtCmsRegistrationInfo *
_DtCmsGetRegistration(
_DtCmsRegistrationInfo **_rlist,
char *client,
u_long prognum,
u_long versnum,
u_long procnum,
int pid)
{
_DtCmsRegistrationInfo *rlist = *_rlist,
*prev,
*ptr,
*tmp_ptr;
for (prev = ptr = rlist; ptr != NULL; ) {
if ((strcmp(ptr->client, client)==0) &&
(ptr->prognum == prognum) &&
(ptr->versnum == versnum) &&
(ptr->procnum == procnum))
{
if (ptr->pid == pid) {
*_rlist = rlist;
return (ptr);
} else {
/* ptr points to a stale record
* remove it from the linked list
* and update ptr and prev appropriately
*/
if (ptr == prev) { /* top of list */
rlist = rlist->next;
_DtCmsFreeRegistrationInfo(ptr);
ptr = prev = rlist;
} else {
prev->next = ptr->next;
tmp_ptr = ptr;
ptr = ptr->next;
_DtCmsFreeRegistrationInfo(tmp_ptr);
}
}
} else {
prev = ptr;
ptr = ptr->next;
}
}
*_rlist = rlist;
return (NULL);
}
/*
* Go through the registration list and ping each client.
* Deregister clients that do not respond.
*/
extern _DtCmsRegistrationInfo *
_DtCmsCheckRegistrationList(_DtCmsRegistrationInfo *rlist)
{
int nclients=0, ndereg=0;
char *sourcehost=NULL;
_DtCmsRegistrationInfo *p_next;
_DtCmsRegistrationInfo *p_prev;
_DtCmsRegistrationInfo *head;
struct timeval timeout_tv;
CLIENT *cl;
boolean_t advance = B_TRUE;
timeout_tv.tv_sec = 10;
timeout_tv.tv_usec = 0;
/* loop through the registration list */
head = p_prev = rlist;
while (rlist != NULL) {
p_next = rlist->next;
if (debug) {
fprintf(stderr,
"%s: pinging %s on prog: %ld, vers: %ld, proc: %ld\n",
pgname, rlist->client, rlist->prognum, rlist->versnum,
rlist->procnum);
}
sourcehost = _DtCmsTarget2Location(rlist->client);
cl = clnt_create(sourcehost, rlist->prognum, rlist->versnum,
"udp");
if (cl != NULL) {
clnt_control(cl, CLSET_TIMEOUT, (char *)&timeout_tv);
timeout_tv.tv_sec = 5;
clnt_control(cl, CLSET_RETRY_TIMEOUT,
(char*)&timeout_tv);
}
/* no client or client not responding */
if (cl == NULL || clnt_call(cl, 0, (xdrproc_t)xdr_void, (char *)NULL,
(xdrproc_t)xdr_void, (char *)NULL, timeout_tv) != RPC_SUCCESS)
{
if (debug) {
clnt_pcreateerror(sourcehost);
fprintf(stderr, "%s: %s deregistered, pid %d\n",
pgname, rlist->client, rlist->pid);
}
if (rlist == p_prev) { /* top of list */
head = p_next;
p_prev = p_next;
advance = B_FALSE;
} else {
p_prev->next = p_next;
}
/* deregister client */
_DtCmsFreeRegistrationInfo(rlist);
rlist = p_prev;
ndereg++;
}
if (cl)
clnt_destroy(cl);
free(sourcehost);
nclients++;
if (advance) {
p_prev = rlist;
rlist = p_next;
} else
advance = B_TRUE;
}
if (debug) {
fprintf(stderr, "%s: number of clients before cleanup = %d\n",
pgname, nclients);
fprintf(stderr, "%s: number of clients deregistered = %d\n",
pgname, ndereg);
}
return (head);
}
extern _DtCmsRegistrationInfo *
_DtCmsDoV1CbForV4Data(
_DtCmsRegistrationInfo *rlist,
char *source,
int pid,
cms_key *key1,
cms_key *key2)
{
Appt_4 appt1, appt2;
if (rlist == NULL)
return (rlist);
appt1.appt_id.tick = key1->time;
appt1.appt_id.key = key1->id;
if (key2) {
appt2.appt_id.tick = key2->time;
appt2.appt_id.key = key2->id;
appt2.next = NULL;
appt1.next = &appt2;
} else
appt1.next = NULL;
return (_DtCmsDoV1Callback(rlist, source, pid, &appt1));
}
/*
* this routine takes care of callbacks to clients using v1 of the callback
* protocol.
*/
extern _DtCmsRegistrationInfo *
_DtCmsDoV1Callback(
_DtCmsRegistrationInfo *rlist,
char *source,
int pid,
Appt_4 *a)
{
Uid_4 *k, *ids = NULL;
Table_Res_4 res;
int nclients=0, ncallbacks=0;
char *sourcehost=NULL;
_DtCmsRegistrationInfo *ptr;
_DtCmsRegistrationInfo *prev;
struct timeval timeout_tv;
CLIENT *cl;
boolean_t advance = B_TRUE;
if (rlist == NULL)
return (rlist);
/* Callback with appointment ids only for security reason. */
while (a != NULL)
{
if ((k = (Uid_4 *)malloc(sizeof(Uid_4))) == NULL) {
_DtCm_free_keyentry4(ids);
return (rlist);
}
k->appt_id = a->appt_id;
k->next = ids;
ids = k;
a = a->next;
}
res.status = access_ok_4;
res.res.tag = ID_4;
res.res.Table_Res_List_4_u.i = ids;
rlist = _DtCmsDoCallback(rlist, 0, source, pid, AGENTVERS, (void *)&res);
if (ids != NULL)
_DtCm_free_keyentry4(ids);
return (rlist);
}
extern void
_DtCmsListRegistration(_DtCmsRegistrationInfo *rlist, char *cal)
{
int n = 0;
fprintf(stderr, "registration list of calendar %s\n", cal);
while(rlist != NULL) {
n++;
fprintf(stderr, "\t%s (pid %d)\n", rlist->client, rlist->pid);
rlist = rlist->next;
}
fprintf(stderr, "\tnumber of registered clients = %d\n", n);
}
extern _DtCmsRegistrationInfo *
_DtCmsRemoveRegistration(
_DtCmsRegistrationInfo *rlist,
_DtCmsRegistrationInfo *rinfo)
{
_DtCmsRegistrationInfo *ptr, *prev;
for (ptr = prev = rlist; ptr != NULL; prev = ptr, ptr = ptr->next) {
if (ptr == rinfo) {
if (ptr == rlist)
rlist = ptr->next;
else
prev->next = ptr->next;
_DtCmsFreeRegistrationInfo(ptr);
break;
}
}
return (rlist);
}
extern _DtCmsRegistrationInfo *
_DtCmsDoOpenCalCallback(
_DtCmsRegistrationInfo *rlist,
char *cal,
char *user,
int pid)
{
cmcb_update_callback_args args;
char calendar[BUFSIZ];
if (rlist == NULL)
return (rlist);
sprintf(calendar, "%s@%s", cal, _DtCmGetLocalHost());
args.calendar = calendar;
args.user = user;
args.data.reason = CSA_CB_CALENDAR_LOGON;
return (_DtCmsDoCallback(rlist, CSA_CB_CALENDAR_LOGON, user, pid,
AGENTVERS_2, (void *)&args));
}
extern _DtCmsRegistrationInfo *
_DtCmsDoRemoveCalCallback(
_DtCmsRegistrationInfo *rlist,
char *cal,
char *user,
int pid)
{
cmcb_update_callback_args args;
char calendar[BUFSIZ];
if (rlist == NULL)
return (rlist);
sprintf(calendar, "%s@%s", cal, _DtCmGetLocalHost());
args.calendar = calendar;
args.user = user;
args.data.reason = CSA_CB_CALENDAR_DELETED;
return (_DtCmsDoCallback(rlist, CSA_CB_CALENDAR_DELETED, user, pid,
AGENTVERS_2, (void *)&args));
}
extern _DtCmsRegistrationInfo *
_DtCmsDoUpdateCalAttrsCallback(
_DtCmsRegistrationInfo *rlist,
char *cal,
char *user,
uint num_attrs,
cms_attribute *attrs,
int pid)
{
cmcb_update_callback_args args;
cmcb_cal_attr_data cdata;
_DtCmsRegistrationInfo *res;
char buf[80];
char calendar[BUFSIZ];
int i;
if (rlist == NULL)
return (rlist);
sprintf(calendar, "%s@%s", cal, _DtCmGetLocalHost());
/* set up update info */
if (num_attrs > 0 &&
(cdata.names = (char **)calloc(1, sizeof(char *)*num_attrs)))
{
for (i = 0; i < num_attrs; i++)
cdata.names[i] = attrs[i].name.name;
} else {
cdata.num_names = 0;
cdata.names = NULL;
}
args.calendar = calendar;
args.user = user;
args.data.reason = CSA_CB_CALENDAR_ATTRIBUTE_UPDATED;
args.data.data.cdata = &cdata;
res = _DtCmsDoCallback(rlist, CSA_CB_CALENDAR_ATTRIBUTE_UPDATED, user,
pid, AGENTVERS_2, (void *)&args);
if (num_attrs > 0) free(cdata.names);
return (res);
}
extern _DtCmsRegistrationInfo *
_DtCmsDoInsertEntryCallback(
_DtCmsRegistrationInfo *rlist,
char *cal,
char *source,
long id,
int pid)
{
cmcb_update_callback_args args;
cmcb_add_entry_data adata;
char buf[80];
char calendar[BUFSIZ];
if (rlist == NULL)
return (rlist);
sprintf(calendar, "%s@%s", cal, _DtCmGetLocalHost());
/* set up update info */
sprintf(buf, "%ld", id);
adata.id = buf;
args.calendar = calendar;
args.user = source;
args.data.reason = CSA_CB_ENTRY_ADDED;
args.data.data.adata = &adata;
return (_DtCmsDoCallback(rlist, CSA_CB_ENTRY_ADDED, source, pid,
AGENTVERS_2, (void *)&args));
}
extern _DtCmsRegistrationInfo *
_DtCmsDoDeleteEntryCallback(
_DtCmsRegistrationInfo *rlist,
char *cal,
char *source,
long id,
int scope,
time_t time,
int pid)
{
cmcb_update_callback_args args;
cmcb_delete_entry_data ddata;
char buf[80];
char calendar[BUFSIZ];
if (rlist == NULL)
return (rlist);
sprintf(calendar, "%s@%s", cal, _DtCmGetLocalHost());
/* set up update info */
sprintf(buf, "%ld", id);
ddata.id = buf;
ddata.scope = scope;
ddata.time = time;
args.calendar = calendar;
args.user = source;
args.data.reason = CSA_CB_ENTRY_DELETED;
args.data.data.ddata = &ddata;
return (_DtCmsDoCallback(rlist, CSA_CB_ENTRY_DELETED, source, pid,
AGENTVERS_2, (void *)&args));
}
extern _DtCmsRegistrationInfo *
_DtCmsDoUpdateEntryCallback(
_DtCmsRegistrationInfo *rlist,
char *cal,
char *source,
long newid,
long oldid,
int scope,
long time,
int pid)
{
cmcb_update_callback_args args;
cmcb_update_entry_data udata;
char nbuf[80], obuf[80];
char calendar[BUFSIZ];
if (rlist == NULL)
return (rlist);
sprintf(calendar, "%s@%s", cal, _DtCmGetLocalHost());
/* set up update info */
sprintf(obuf, "%ld", oldid);
udata.oldid = obuf;
if (newid > 0)
sprintf(nbuf, "%ld", newid);
else
nbuf[0] = '\0';
udata.newid = nbuf;
udata.scope = scope;
udata.time = time;
args.calendar = calendar;
args.user = source;
args.data.reason = CSA_CB_ENTRY_UPDATED;
args.data.data.udata = &udata;
return (_DtCmsDoCallback(rlist, CSA_CB_ENTRY_UPDATED, source, pid,
AGENTVERS_2, (void *)&args));
}
/*
* this routine takes care of callbacks to clients using either
* v1 or v2 of the callback protocol.
*/
static _DtCmsRegistrationInfo *
_DtCmsDoCallback(
_DtCmsRegistrationInfo *rlist,
uint type,
char *source,
int pid,
int version,
void *args)
{
int nclients=0, ncallbacks=0;
char *sourcehost=NULL;
_DtCmsRegistrationInfo *ptr;
_DtCmsRegistrationInfo *prev;
struct timeval timeout_tv;
CLIENT *cl;
boolean_t advance = B_TRUE;
/*
* loop through the registration list looking for parties
* interested in this transaction.
*/
for (ptr = prev = rlist; ptr != NULL; ) {
/* The caller will get the results of the rpc call.
* If he's registered on the callback list, don't call him -
* UNLESS the process id of his client differs from the
* original ticket. However, if the pid is a VOIDPID (-1),
* a version 2 client has registered and there's no way
* of telling which instance of the client it is. So,
* to be safe (avoid deadlock) we won't callback version
* 2 clients registered on version 3 daemons if their
* registration name entry matches the caller's. [Nanno]
*/
if (version != ptr->versnum ||
(type && !(type & ptr->types)) ||
((strcmp(source, ptr->client) == 0) &&
((pid == ptr->pid) || (pid == -1 ) || (ptr->pid == -1)))) {
prev = ptr;
ptr = ptr->next;
nclients++;
continue;
}
sourcehost = _DtCmsTarget2Location(ptr->client);
if (debug) {
fprintf(stderr,
"%s: calling back %s on prog: %ld, vers: %ld, proc: %ld\n",
pgname, ptr->client, ptr->prognum, ptr->versnum,
ptr->procnum);
}
cl = clnt_create(sourcehost, ptr->prognum, ptr->versnum, "udp");
/* deregister client if fails to create handle */
if (cl == NULL) {
if (debug) {
clnt_pcreateerror(sourcehost);
}
if (ptr == rlist) { /* top of list */
rlist = ptr->next;
prev = rlist;
advance = B_FALSE;
} else {
prev->next = ptr->next;
}
/* deregister client */
_DtCmsFreeRegistrationInfo(ptr);
ptr = prev;
} else {
/* Set timeout to zero so that the call
* returns right away.
*/
timeout_tv.tv_sec = 0;
timeout_tv.tv_usec = 0;
#ifndef SunOS
/* for non-sun systems, clnt_call won't
* return right away unless timeout is set
* to zero using clnt_control(), (rpc bug?)
*/
clnt_control(cl, CLSET_TIMEOUT,
(char *)&timeout_tv);
#endif
if (version == AGENTVERS) {
(void)clnt_call(cl, ptr->procnum,
(xdrproc_t)_DtCm_xdr_Table_Res_4, (char *)args,
(xdrproc_t)xdr_void, (char *)0, timeout_tv);
} else if (version == AGENTVERS_2) {
(void)clnt_call(cl, ptr->procnum,
(xdrproc_t)xdr_cmcb_update_callback_args,
(char *)args, (xdrproc_t)xdr_void, (char *)0,
timeout_tv);
}
ncallbacks++;
nclients++;
}
if (cl)
clnt_destroy(cl);
free(sourcehost);
if (advance) {
prev = ptr;
ptr = ptr->next;
} else
advance = B_TRUE;
}
if (debug) {
fprintf(stderr, "%s: number of registered clients = %d\n",
pgname, nclients);
fprintf(stderr, "%s: number of clients called back= %d\n",
pgname, ncallbacks);
}
return (rlist);
}