/* * 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 /* NOTE: sbport.h must be the first include. */ #include #include #include #include #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(ncharsoffset += 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); }