Files
cdesktop/cde/lib/DtSvc/DtEncap/spc-obj.c
2018-05-31 22:23:19 -06:00

700 lines
18 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
*/
/*
* File: spc-obj.c $TOG: spc-obj.c /main/6 1997/12/29 10:43:29 bill $
* Language: C
*
* (c) Copyright 1989, Hewlett-Packard Company, all rights reserved.
*
* (c) Copyright 1993, 1994 Hewlett-Packard Company *
* (c) Copyright 1993, 1994 International Business Machines Corp. *
* (c) Copyright 1993, 1994 Sun Microsystems, Inc. *
* (c) Copyright 1993, 1994 Novell, Inc. *
*/
#include <bms/sbport.h> /* NOTE: sbport.h must be the first include. */
#include <signal.h>
#include <SPC/spcP.h>
#include <bms/MemoryMgr.h>
#include <SPC/spc-proto.h>
#include "DtSvcLock.h"
/* global declarations */
/* SPC_Initialized is in bmsglob.c */
extern int SPC_Initialized;
/* external declarations */
extern SPC_Channel_Ptr spc_activation_list;
extern XeString spc_user_environment_file;
/*
* Global variable to specifying whether the process using this
* library is a SPC client or a SPC daemon. If the process is a
* client, the SIGCLD signal handler will not be installed.
* However, if the process is the daemon, the signal handler will
* be installed.
*
* This will be set to 'SPC_I_AM_A_DAEMON' by the spcd process.
*/
int SPC_who_am_i = SPC_I_AM_A_CLIENT;
/* Initialization functions for class objects */
/*----------------------------------------------------------------------+*/
object *alloc_channel_object(object_clasp c)
/*----------------------------------------------------------------------+*/
{
object *p=(object *) XeMalloc((unsigned) c->object_size);
memset(p, 0, (int) c->object_size);
return(p);
}
/*----------------------------------------------------------------------+*/
void channel_class_init(object_clasp t)
/*----------------------------------------------------------------------+*/
{
channel_clasp c = (channel_clasp) t;
c->new_obj = alloc_channel_object;
c->open = open_channel_object;
c->close = close_channel_object;
c->read = read_channel_object;
c->write = write_channel_object;
c->reset = reset_channel_object;
c->pre_fork = pre_fork_channel_object;
c->post_fork = post_fork_channel_object;
c->exec_proc = exec_proc_channel_object;
c->signal = signal_channel_object;
c->wait_for_termination = channel_object_wait_for_termination;
c->attach = attach_channel_object;
c->input = NULL;
c->add_input = add_input_channel_object;
c->remove_logfile = remove_logfile_channel_object;
}
static struct channel_class channel_class_struct = {
(root_clasp) &root_class, /* base class pointer */
"channel", /* class name */
channel_class_init, /* class initialize function */
sizeof(SPC_Channel), /* size */
0
};
channel_clasp channel_class = &channel_class_struct;
static Wire dummy_wire={
0, /* Flags */
-1, -1, /* File Descriptors */
(XeString) "/dev/null", /* Master PTY */
(XeString) "/dev/null", /* Slave PTY */
0, 0, /* Toolkit IDs */
0 /* pointer to next wire */
};
/*----------------------------------------------------------------------+*/
int
SPC_ResetTerminator(void)
/*----------------------------------------------------------------------+*/
{
struct sigaction svect;
_DtSvcProcessLock();
if (SPC_who_am_i == SPC_I_AM_A_DAEMON) {
svect.sa_handler = SPC_Child_Terminated;
sigemptyset(&svect.sa_mask);
svect.sa_flags = 0;
if(sigaction(SIGCHLD, &svect, (struct sigaction *)NULL)==ERROR) {
SPC_Error(SPC_No_Signal_Handler);
return(SPC_ERROR);
}
}
_DtSvcProcessUnlock();
return (TRUE);
}
/*----------------------------------------------------------------------+*/
int
SPC_Initialize(void)
/*----------------------------------------------------------------------+*/
{
XeString home;
_DtSvcProcessLock();
if(SPC_Initialized) {
_DtSvcProcessUnlock();
return(TRUE);
}
spc_init_fds();
if (!SPC_ResetTerminator()) {
_DtSvcProcessUnlock();
return(SPC_ERROR);
}
if(!SPC_Init_Local_Host_Info()) {
_DtSvcProcessUnlock();
return(SPC_ERROR);
}
if(SPC_Setup_Synchronous_Terminator()==SPC_ERROR) {
_DtSvcProcessUnlock();
return(SPC_ERROR);
}
if(home=getenv("HOME")) {
spc_user_environment_file=(XeString) XeMalloc(strlen(home)+
strlen(SPCD_ENV_HOME_DIRECTORY)+strlen(SPCD_ENV_FILE)+3);
sprintf(spc_user_environment_file, "%s/%s/%s",
home, SPCD_ENV_HOME_DIRECTORY, SPCD_ENV_FILE);
}
SPC_Initialized=TRUE;
_DtSvcProcessUnlock();
return(TRUE);
}
/*
**
** SPC_Initialize_Channel will create & return a channel object,
** based on the values of hostname and iomode.
**
*/
/*----------------------------------------------------------------------+*/
SPC_Channel_Ptr SPC_Initialize_Channel(XeString hostname,
int iomode)
/*----------------------------------------------------------------------+*/
{
SPC_Channel_Ptr channel = NULL;
/* Check for local or remote machine. If remote, create a
remote channel object */
if (!SPC_Local_Hostname(hostname)) {
channel=(SPC_Channel_Ptr)object_create((object_clasp)remote_channel_class);
} else {
/* We are local. Create the appropriate object. */
if(IS_SPCIO_NOIOMODE(iomode))
channel=(SPC_Channel_Ptr)object_create((object_clasp)noio_channel_class);
if(IS_SPCIO_PIPE(iomode))
channel=(SPC_Channel_Ptr)object_create((object_clasp)pipe_channel_class);
if(IS_SPCIO_PTY(iomode))
channel=(SPC_Channel_Ptr)object_create((object_clasp)pty_channel_class);
}
return(channel);
}
/*
**
** SPC_Channel_Terminated will do any work necessary on a channel when
** we detect that a subprocess has terminated.
**
*/
/*----------------------------------------------------------------------+*/
void SPC_Channel_Terminated(SPC_Channel_Ptr channel)
/*----------------------------------------------------------------------+*/
{
int type, cause;
SPC_Change_State(channel, 0, -1, 0);
/* Set the close timeout. If we are on a PTY, we will return
after two seconds if we are waiting for EOF */
channel->close_timeout=2;
if(IS_DATA(channel) && (channel->Input_Handler)) {
while(IS_SPCIO_DATA(channel->wires[STDOUT]->flags))
SPC_Input_Handler(channel, STDOUT);
while(IS_SPCIO_DATA(channel->wires[STDERR]->flags))
SPC_Input_Handler(channel, STDERR);
}
if(channel->Terminate_Handler) {
XeSPCGetProcessStatus(channel, &type, &cause);
(* channel->Terminate_Handler)
(channel, channel->pid, type, cause, channel->Terminate_Data);
}
channel->close_timeout=0;
}
/*
**
** SPC_Check style makes sure that we have a legal IOMode.
**
*/
/*----------------------------------------------------------------------+*/
int
SPC_Check_Style(int iomode)
/*----------------------------------------------------------------------+*/
{
int stylecount=0;
/* First, make sure that we have only one style bit set */
/*** NOTE - We can probably do something more tricky here, to be more
efficient. However, I am going to do this the slow way to be safe */
if(IS_SPCIO_NOIOMODE(iomode))
stylecount++;
if(IS_SPCIO_PIPE(iomode))
stylecount++;
if(IS_SPCIO_PTY(iomode))
stylecount++;
if(stylecount != 1) {
SPC_Error(SPC_Illegal_Iomode);
return(SPC_ERROR);
}
/* Okay, now check to make sure we don't have any conflicting
modes set */
if ((IS_SPCIO_LINEEDIT(iomode) && IS_SPCIO_PIPE(iomode)) ||
(IS_SPCIO_PTY(iomode) && IS_SPCIO_WAIT(iomode) &&
!IS_SPCIO_TOOLKIT(iomode)) ||
(!IS_SPCIO_NOIO(iomode) && IS_SPCIO_USE_LOGFILE(iomode))
)
{
SPC_Error(SPC_Illegal_Iomode);
return(SPC_ERROR);
}
return(TRUE);
}
/*
**
** SPC_Transform_Iomode will transform a user-specified iomode into
** one that is suitable for use by SPC. It will then check the new
** iomode to make sure that it is legal.
**
*/
/*----------------------------------------------------------------------+*/
int
SPC_Transform_Iomode(int iomode)
/*----------------------------------------------------------------------+*/
{
if(IS_SPCIO_NOIO(iomode))
iomode |= SPCIO_NOIOMODE;
if(IS_SPCIO_DEFAULT(iomode))
iomode |= SPCIO_DEFAULT;
if(IS_SPCIO_TOOLKIT(iomode))
iomode |= SPCIO_SYNC_TERMINATOR;
/* Check to make sure that the iomode is consistent */
if(SPC_Check_Style(iomode)==SPC_ERROR)
return(SPC_ERROR);
return(iomode);
}
/*
**
** SPC_Newline_Filter will return only lines that end in newlines, or
** 'ntoread' characters if no newline is found in time. It will also
** return as many characters as have been read in the case of EOF.
**
*/
/*----------------------------------------------------------------------+*/
int
SPC_Newline_Filter(SPC_Channel_Ptr channel,
int connector,
XeString buffer,
int ntoread)
/*----------------------------------------------------------------------+*/
{
buffered_data_ptr cbuf;
XeString usrptr;
XeString cbufptr;
int nchars, nlcopied, scalarlen, nchars_this_buffer;
if(!(cbuf=channel->linebufs[connector])) {
if((cbuf=SPC_New_Buffered_Data_Ptr())==SPC_ERROR)
return(SPC_ERROR);
channel->linebufs[connector]=cbuf;
}
usrptr=buffer;
cbufptr=cbuf->data+cbuf->offset;
nchars=0;
nlcopied = FALSE;
channel->IOMode &= ~SPCIO_HAS_DATA;
do {
nchars_this_buffer=0;
scalarlen=cbuf->len;
while(nchars<ntoread && nchars_this_buffer<scalarlen && !nlcopied) {
nlcopied = (*cbufptr == Newline);
*usrptr++ = (*cbufptr++);
nchars++;
nchars_this_buffer++;
}
if(nchars == ntoread || nlcopied) {
cbuf->offset += nchars_this_buffer;
cbuf->len -= nchars_this_buffer;
if(strchr(cbuf->data+cbuf->offset, Newline))
channel->IOMode |= SPCIO_HAS_DATA;
return(nchars);
}
cbufptr = cbuf->data;
cbuf->offset = 0;
do {
cbuf->len=mempf3(channel, read, connector, cbufptr, SPC_BUFSIZ);
} while(cbuf->len == (EXCEPT_FLAG));
cbufptr[cbuf->len]=0;
} while((cbuf->len) > 0);
return(nchars);
}
/*----------------------------------------------------------------------+*/
int
SPC_Input_Handler(SPC_Channel_Ptr channel,
int connector)
/*----------------------------------------------------------------------+*/
{
int nchars;
XeChar spc_iobuffer[SPC_BUFSIZ+1];
channel->IOMode &= ~SPCIO_HAS_DATA;
do {
nchars=(*channel->read_filter)
(channel, connector, spc_iobuffer, SPC_BUFSIZ);
/* Check nchars. If it is EXCEPT_FLAG, we had a special occurrence (such
as an ioctl on a PTY). In any case, don't do any more processing */
if(nchars==EXCEPT_FLAG)
return(FALSE);
/* Call Read handlers */
spc_iobuffer[nchars]=XeChar_NULL;
if(channel->Input_Handler)
(* channel->Input_Handler)
(channel->client_data, spc_iobuffer, nchars, connector);
} while(HAS_DATA(channel));
return(nchars);
}
/*
***
*** Method definitions for channel objects
***
*/
/*
* This routine handles initialization which must occur for every channel.
*/
/*----------------------------------------------------------------------+*/
SPC_Channel_Ptr open_channel_object(SPC_Channel_Ptr channel,
int iomode,
XeString UNUSED_PARM(hostname))
/*----------------------------------------------------------------------+*/
{
/* initialize local data structures */
/* If we are doing line-oriented IO, set the read filter
to be the NL filter. Otherwise, set it to be the read
method. */
if(IS_SPCIO_LINEORIENTED(iomode))
channel->read_filter=SPC_Newline_Filter;
else
channel->read_filter=channel->class_ptr->read;
/* this (cid) should probably just be a long... */
channel->cid=(int) ((long)channel & 0xffffffff);
channel->identifier = Channel_Identifier;
channel->IOMode = iomode;
channel->wires[STDIN] = (&dummy_wire);
channel->wires[STDOUT]= (&dummy_wire);
channel->wires[STDERR]= (&dummy_wire);
channel->file_descs[STDIN] = -1;
channel->file_descs[STDOUT]= -1;
channel->file_descs[STDERR]= -1;
channel->wire_list=NULL;
channel->logfile=NULL;
/* Link it into the activation list (at the front for now) */
channel->next = spc_activation_list;
spc_activation_list = channel;
return(channel);
}
/*
**
** This method will get called AFTER the work done in the child methods,
** so's we can deallocate all memory associated with this channel
**
*/
/*----------------------------------------------------------------------+*/
int close_channel_object (SPC_Channel_Ptr channel)
/*----------------------------------------------------------------------+*/
{
Wire *wirelist, *next_wire;
int i;
SPC_Channel_Ptr trail, ptr;
/* Remove the channel from the activation list */
if(spc_activation_list == channel)
spc_activation_list = channel->next;
else {
trail = spc_activation_list;
while(trail) {
ptr = trail->next;
if(ptr == channel) {
trail->next = ptr->next;
break;
}
trail=ptr;
}
if(!trail) {
SPC_Error(SPC_Closed_Channel);
return(SPC_ERROR);
}
}
/* Deallocate any memory allocated to the subfields */
if(IS_SPCIO_DEALLOC_ARGV(channel->IOMode))
SPC_Free_Envp(channel->argv);
SPC_Free_Envp(channel->envp);
wirelist=channel->wire_list;
while(wirelist) {
next_wire=wirelist->next;
free_wire(wirelist);
wirelist=next_wire;
}
for(i=1; i<3; i++)
if(channel->linebufs[i])
free((char *)channel->linebufs[i]);
/* Free the queue associated with the channel */
SPC_Flush_Queued_Data(channel);
Xe_release_queue(channel->queued_remote_data);
/* Deallocate the channel */
free((char *)channel);
return(TRUE);
}
/*----------------------------------------------------------------------+*/
int read_channel_object(SPC_Channel_Ptr UNUSED_PARM(channel),
int UNUSED_PARM(connector), /* STDOUT or STDERR */
XeString UNUSED_PARM(buffer),
int UNUSED_PARM(nbytes))
/*----------------------------------------------------------------------+*/
{
/* need to check consistency between connector and READ/WRITE/ERROR here */
return(TRUE);
}
/*----------------------------------------------------------------------+*/
int write_channel_object(SPC_Channel_Ptr UNUSED_PARM(channel),
XeString UNUSED_PARM(buffer),
int UNUSED_PARM(nbytes))
/*----------------------------------------------------------------------+*/
{
/* check for consistent arguments (channel open for WRITE) */
return(TRUE);
}
/*----------------------------------------------------------------------+*/
int reset_channel_object(SPC_Channel_Ptr channel)
/*----------------------------------------------------------------------+*/
{
channel->IOMode &= ~SPCIO_DATA;
return(TRUE);
}
/*----------------------------------------------------------------------+*/
int pre_fork_channel_object(SPC_Channel_Ptr channel)
/*----------------------------------------------------------------------+*/
{
Wire *wirelist;
int flag=0;
/* Set all wires to be "data ready" */
for(wirelist=channel->wire_list; wirelist; wirelist=wirelist->next) {
wirelist->flags |= SPCIO_DATA;
flag=1;
}
/* Move to the "Running & (possibly) data ready" state */
SPC_Change_State(channel, 0, flag, 1);
return(TRUE);
}
/*----------------------------------------------------------------------+*/
int post_fork_channel_object(SPC_Channel_Ptr UNUSED_PARM(channel),
int UNUSED_PARM(parentp))
/*----------------------------------------------------------------------+*/
{
return(TRUE);
}
/*----------------------------------------------------------------------+*/
int exec_proc_channel_object (SPC_Channel_Ptr channel)
/*----------------------------------------------------------------------+*/
{
XeString *tmp_argv;
int iomode=channel->IOMode;
/* If there is no argv specified, fix it up to be the convention
(argv[0] = file pathname) */
if (channel->argv == NULL) {
tmp_argv=Alloc_Argv(2);
if(tmp_argv==SPC_ERROR)
return(SPC_ERROR);
tmp_argv[0]=SPC_copy_string(channel->path);
tmp_argv[1]=NULL;
channel->argv = tmp_argv;
channel->IOMode |= SPCIO_DEALLOC_ARGV;
}
if(IS_SPCIO_WAIT(channel->IOMode))
XeSPCRegisterTerminator(channel, NULL, NULL);
return(TRUE);
}
/*----------------------------------------------------------------------+*/
int
signal_channel_object (SPC_Channel_Ptr UNUSED_PARM(channel),
int UNUSED_PARM(sig))
/*----------------------------------------------------------------------+*/
{
return(TRUE);
}
/*----------------------------------------------------------------------+*/
int channel_object_wait_for_termination(SPC_Channel_Ptr UNUSED_PARM(channel))
/*----------------------------------------------------------------------+*/
{
return(TRUE);
}
/*----------------------------------------------------------------------+*/
int
attach_channel_object(SPC_Channel_Ptr UNUSED_PARM(channel),
int UNUSED_PARM(pid))
/*----------------------------------------------------------------------+*/
{
return(TRUE);
}
/*----------------------------------------------------------------------+*/
int
add_input_channel_object(SPC_Channel_Ptr UNUSED_PARM(channel),
SbInputHandlerProc UNUSED_PARM(handler),
void *UNUSED_PARM(data) )
/*----------------------------------------------------------------------+*/
{
return(TRUE);
}
/*----------------------------------------------------------------------+*/
int
remove_logfile_channel_object(SPC_Channel_Ptr channel)
/*----------------------------------------------------------------------+*/
{
if(IS_SPCIO_USE_LOGFILE(channel->IOMode))
return(TRUE);
else
return(FALSE);
}