2 * CDE - Common Desktop Environment
4 * Copyright (c) 1993-2012, The Open Group. All rights reserved.
6 * These libraries and programs are free software; you can
7 * redistribute them and/or modify them under the terms of the GNU
8 * Lesser General Public License as published by the Free Software
9 * Foundation; either version 2 of the License, or (at your option)
12 * These libraries and programs are distributed in the hope that
13 * they will be useful, but WITHOUT ANY WARRANTY; without even the
14 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU Lesser General Public License for more
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with these libraries and programs; if not, write
20 * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21 * Floor, Boston, MA 02110-1301 USA
24 * File: spc-obj.c $TOG: spc-obj.c /main/6 1997/12/29 10:43:29 bill $
27 * (c) Copyright 1989, Hewlett-Packard Company, all rights reserved.
29 * (c) Copyright 1993, 1994 Hewlett-Packard Company *
30 * (c) Copyright 1993, 1994 International Business Machines Corp. *
31 * (c) Copyright 1993, 1994 Sun Microsystems, Inc. *
32 * (c) Copyright 1993, 1994 Novell, Inc. *
35 #include <bms/sbport.h> /* NOTE: sbport.h must be the first include. */
39 #include <bms/MemoryMgr.h>
41 #include <SPC/spc-proto.h>
42 #include "DtSvcLock.h"
44 /* global declarations */
46 /* SPC_Initialized is in bmsglob.c */
48 extern int SPC_Initialized;
50 /* external declarations */
52 extern SPC_Channel_Ptr spc_activation_list;
53 extern XeString spc_user_environment_file;
56 * Global variable to specifying whether the process using this
57 * library is a SPC client or a SPC daemon. If the process is a
58 * client, the SIGCLD signal handler will not be installed.
59 * However, if the process is the daemon, the signal handler will
62 * This will be set to 'SPC_I_AM_A_DAEMON' by the spcd process.
64 int SPC_who_am_i = SPC_I_AM_A_CLIENT;
66 /* Initialization functions for class objects */
69 /*----------------------------------------------------------------------+*/
70 object *alloc_channel_object(object_clasp c)
71 /*----------------------------------------------------------------------+*/
73 object *p=(object *) XeMalloc((unsigned) c->object_size);
74 memset(p, 0, (int) c->object_size);
78 /*----------------------------------------------------------------------+*/
79 void channel_class_init(object_clasp t)
80 /*----------------------------------------------------------------------+*/
83 channel_clasp c = (channel_clasp) t;
85 c->new_obj = alloc_channel_object;
87 c->open = open_channel_object;
88 c->close = close_channel_object;
89 c->read = read_channel_object;
90 c->write = write_channel_object;
91 c->reset = reset_channel_object;
92 c->pre_fork = pre_fork_channel_object;
93 c->post_fork = post_fork_channel_object;
94 c->exec_proc = exec_proc_channel_object;
95 c->signal = signal_channel_object;
96 c->wait_for_termination = channel_object_wait_for_termination;
97 c->attach = attach_channel_object;
99 c->add_input = add_input_channel_object;
100 c->remove_logfile = remove_logfile_channel_object;
103 static struct channel_class channel_class_struct = {
104 (root_clasp) &root_class, /* base class pointer */
105 "channel", /* class name */
106 channel_class_init, /* class initialize function */
107 sizeof(SPC_Channel), /* size */
111 channel_clasp channel_class = &channel_class_struct;
113 static Wire dummy_wire={
115 -1, -1, /* File Descriptors */
116 (XeString) "/dev/null", /* Master PTY */
117 (XeString) "/dev/null", /* Slave PTY */
118 0, 0, /* Toolkit IDs */
119 0 /* pointer to next wire */
122 /*----------------------------------------------------------------------+*/
124 SPC_ResetTerminator(void)
125 /*----------------------------------------------------------------------+*/
127 struct sigaction svect;
130 if (SPC_who_am_i == SPC_I_AM_A_DAEMON) {
131 svect.sa_handler = SPC_Child_Terminated;
132 sigemptyset(&svect.sa_mask);
135 if(sigaction(SIGCHLD, &svect, (struct sigaction *)NULL)==ERROR) {
136 SPC_Error(SPC_No_Signal_Handler);
141 _DtSvcProcessUnlock();
145 /*----------------------------------------------------------------------+*/
148 /*----------------------------------------------------------------------+*/
154 if(SPC_Initialized) {
155 _DtSvcProcessUnlock();
161 if (!SPC_ResetTerminator()) {
162 _DtSvcProcessUnlock();
166 if(!SPC_Init_Local_Host_Info()) {
167 _DtSvcProcessUnlock();
171 if(SPC_Setup_Synchronous_Terminator()==SPC_ERROR) {
172 _DtSvcProcessUnlock();
176 if(home=getenv("HOME")) {
177 spc_user_environment_file=(XeString) XeMalloc(strlen(home)+
178 strlen(SPCD_ENV_HOME_DIRECTORY)+strlen(SPCD_ENV_FILE)+3);
179 sprintf(spc_user_environment_file, "%s/%s/%s",
180 home, SPCD_ENV_HOME_DIRECTORY, SPCD_ENV_FILE);
183 SPC_Initialized=TRUE;
184 _DtSvcProcessUnlock();
191 ** SPC_Initialize_Channel will create & return a channel object,
192 ** based on the values of hostname and iomode.
196 /*----------------------------------------------------------------------+*/
197 SPC_Channel_Ptr SPC_Initialize_Channel(XeString hostname,
199 /*----------------------------------------------------------------------+*/
201 SPC_Channel_Ptr channel = NULL;
203 /* Check for local or remote machine. If remote, create a
204 remote channel object */
205 if (!SPC_Local_Hostname(hostname)) {
206 channel=(SPC_Channel_Ptr)object_create((object_clasp)remote_channel_class);
210 /* We are local. Create the appropriate object. */
212 if(IS_SPCIO_NOIOMODE(iomode))
213 channel=(SPC_Channel_Ptr)object_create((object_clasp)noio_channel_class);
215 if(IS_SPCIO_PIPE(iomode))
216 channel=(SPC_Channel_Ptr)object_create((object_clasp)pipe_channel_class);
218 if(IS_SPCIO_PTY(iomode))
219 channel=(SPC_Channel_Ptr)object_create((object_clasp)pty_channel_class);
228 ** SPC_Channel_Terminated will do any work necessary on a channel when
229 ** we detect that a subprocess has terminated.
233 /*----------------------------------------------------------------------+*/
234 void SPC_Channel_Terminated(SPC_Channel_Ptr channel)
235 /*----------------------------------------------------------------------+*/
239 SPC_Change_State(channel, 0, -1, 0);
241 /* Set the close timeout. If we are on a PTY, we will return
242 after two seconds if we are waiting for EOF */
244 channel->close_timeout=2;
246 if(IS_DATA(channel) && (channel->Input_Handler)) {
247 while(IS_SPCIO_DATA(channel->wires[STDOUT]->flags))
248 SPC_Input_Handler(channel, STDOUT);
249 while(IS_SPCIO_DATA(channel->wires[STDERR]->flags))
250 SPC_Input_Handler(channel, STDERR);
253 if(channel->Terminate_Handler) {
254 XeSPCGetProcessStatus(channel, &type, &cause);
255 (* channel->Terminate_Handler)
256 (channel, channel->pid, type, cause, channel->Terminate_Data);
259 channel->close_timeout=0;
265 ** SPC_Check style makes sure that we have a legal IOMode.
269 /*----------------------------------------------------------------------+*/
271 SPC_Check_Style(int iomode)
272 /*----------------------------------------------------------------------+*/
277 /* First, make sure that we have only one style bit set */
279 /*** NOTE - We can probably do something more tricky here, to be more
280 efficient. However, I am going to do this the slow way to be safe */
282 if(IS_SPCIO_NOIOMODE(iomode))
284 if(IS_SPCIO_PIPE(iomode))
286 if(IS_SPCIO_PTY(iomode))
289 if(stylecount != 1) {
290 SPC_Error(SPC_Illegal_Iomode);
294 /* Okay, now check to make sure we don't have any conflicting
297 if ((IS_SPCIO_LINEEDIT(iomode) && IS_SPCIO_PIPE(iomode)) ||
298 (IS_SPCIO_PTY(iomode) && IS_SPCIO_WAIT(iomode) &&
299 !IS_SPCIO_TOOLKIT(iomode)) ||
300 (!IS_SPCIO_NOIO(iomode) && IS_SPCIO_USE_LOGFILE(iomode))
303 SPC_Error(SPC_Illegal_Iomode);
313 ** SPC_Transform_Iomode will transform a user-specified iomode into
314 ** one that is suitable for use by SPC. It will then check the new
315 ** iomode to make sure that it is legal.
319 /*----------------------------------------------------------------------+*/
321 SPC_Transform_Iomode(int iomode)
322 /*----------------------------------------------------------------------+*/
324 if(IS_SPCIO_NOIO(iomode))
325 iomode |= SPCIO_NOIOMODE;
326 if(IS_SPCIO_DEFAULT(iomode))
327 iomode |= SPCIO_DEFAULT;
328 if(IS_SPCIO_TOOLKIT(iomode))
329 iomode |= SPCIO_SYNC_TERMINATOR;
331 /* Check to make sure that the iomode is consistent */
333 if(SPC_Check_Style(iomode)==SPC_ERROR)
342 ** SPC_Newline_Filter will return only lines that end in newlines, or
343 ** 'ntoread' characters if no newline is found in time. It will also
344 ** return as many characters as have been read in the case of EOF.
348 /*----------------------------------------------------------------------+*/
350 SPC_Newline_Filter(SPC_Channel_Ptr channel,
354 /*----------------------------------------------------------------------+*/
356 buffered_data_ptr cbuf;
359 int nchars, nlcopied, scalarlen, nchars_this_buffer;
361 if(!(cbuf=channel->linebufs[connector])) {
362 if((cbuf=SPC_New_Buffered_Data_Ptr())==SPC_ERROR)
364 channel->linebufs[connector]=cbuf;
368 cbufptr=cbuf->data+cbuf->offset;
371 channel->IOMode &= ~SPCIO_HAS_DATA;
375 nchars_this_buffer=0;
378 while(nchars<ntoread && nchars_this_buffer<scalarlen && !nlcopied) {
379 nlcopied = (*cbufptr == Newline);
380 *usrptr++ = (*cbufptr++);
382 nchars_this_buffer++;
385 if(nchars == ntoread || nlcopied) {
386 cbuf->offset += nchars_this_buffer;
387 cbuf->len -= nchars_this_buffer;
388 if(strchr(cbuf->data+cbuf->offset, Newline))
389 channel->IOMode |= SPCIO_HAS_DATA;
393 cbufptr = cbuf->data;
396 cbuf->len=mempf3(channel, read, connector, cbufptr, SPC_BUFSIZ);
397 } while(cbuf->len == (EXCEPT_FLAG));
399 cbufptr[cbuf->len]=0;
401 } while((cbuf->len) > 0);
406 /*----------------------------------------------------------------------+*/
408 SPC_Input_Handler(SPC_Channel_Ptr channel,
410 /*----------------------------------------------------------------------+*/
414 XeChar spc_iobuffer[SPC_BUFSIZ+1];
416 channel->IOMode &= ~SPCIO_HAS_DATA;
420 nchars=(*channel->read_filter)
421 (channel, connector, spc_iobuffer, SPC_BUFSIZ);
423 /* Check nchars. If it is EXCEPT_FLAG, we had a special occurrence (such
424 as an ioctl on a PTY). In any case, don't do any more processing */
426 if(nchars==EXCEPT_FLAG)
429 /* Call Read handlers */
431 spc_iobuffer[nchars]=XeChar_NULL;
433 if(channel->Input_Handler)
434 (* channel->Input_Handler)
435 (channel->client_data, spc_iobuffer, nchars, connector);
437 } while(HAS_DATA(channel));
444 *** Method definitions for channel objects
449 * This routine handles initialization which must occur for every channel.
452 /*----------------------------------------------------------------------+*/
453 SPC_Channel_Ptr open_channel_object(SPC_Channel_Ptr channel,
455 XeString UNUSED_PARM(hostname))
456 /*----------------------------------------------------------------------+*/
460 /* initialize local data structures */
462 /* If we are doing line-oriented IO, set the read filter
463 to be the NL filter. Otherwise, set it to be the read
466 if(IS_SPCIO_LINEORIENTED(iomode))
467 channel->read_filter=SPC_Newline_Filter;
469 channel->read_filter=channel->class_ptr->read;
471 /* this (cid) should probably just be a long... */
472 channel->cid=(int) ((long)channel & 0xffffffff);
473 channel->identifier = Channel_Identifier;
474 channel->IOMode = iomode;
475 channel->wires[STDIN] = (&dummy_wire);
476 channel->wires[STDOUT]= (&dummy_wire);
477 channel->wires[STDERR]= (&dummy_wire);
478 channel->file_descs[STDIN] = -1;
479 channel->file_descs[STDOUT]= -1;
480 channel->file_descs[STDERR]= -1;
481 channel->wire_list=NULL;
482 channel->logfile=NULL;
484 /* Link it into the activation list (at the front for now) */
486 channel->next = spc_activation_list;
487 spc_activation_list = channel;
494 ** This method will get called AFTER the work done in the child methods,
495 ** so's we can deallocate all memory associated with this channel
499 /*----------------------------------------------------------------------+*/
500 int close_channel_object (SPC_Channel_Ptr channel)
501 /*----------------------------------------------------------------------+*/
503 Wire *wirelist, *next_wire;
505 SPC_Channel_Ptr trail, ptr;
507 /* Remove the channel from the activation list */
509 if(spc_activation_list == channel)
510 spc_activation_list = channel->next;
512 trail = spc_activation_list;
516 trail->next = ptr->next;
522 SPC_Error(SPC_Closed_Channel);
527 /* Deallocate any memory allocated to the subfields */
529 if(IS_SPCIO_DEALLOC_ARGV(channel->IOMode))
530 SPC_Free_Envp(channel->argv);
531 SPC_Free_Envp(channel->envp);
533 wirelist=channel->wire_list;
535 next_wire=wirelist->next;
541 if(channel->linebufs[i])
542 free((char *)channel->linebufs[i]);
544 /* Free the queue associated with the channel */
546 SPC_Flush_Queued_Data(channel);
547 Xe_release_queue(channel->queued_remote_data);
549 /* Deallocate the channel */
551 free((char *)channel);
556 /*----------------------------------------------------------------------+*/
557 int read_channel_object(SPC_Channel_Ptr UNUSED_PARM(channel),
558 int UNUSED_PARM(connector), /* STDOUT or STDERR */
559 XeString UNUSED_PARM(buffer),
560 int UNUSED_PARM(nbytes))
561 /*----------------------------------------------------------------------+*/
564 /* need to check consistency between connector and READ/WRITE/ERROR here */
569 /*----------------------------------------------------------------------+*/
570 int write_channel_object(SPC_Channel_Ptr UNUSED_PARM(channel),
571 XeString UNUSED_PARM(buffer),
572 int UNUSED_PARM(nbytes))
573 /*----------------------------------------------------------------------+*/
576 /* check for consistent arguments (channel open for WRITE) */
581 /*----------------------------------------------------------------------+*/
582 int reset_channel_object(SPC_Channel_Ptr channel)
583 /*----------------------------------------------------------------------+*/
585 channel->IOMode &= ~SPCIO_DATA;
590 /*----------------------------------------------------------------------+*/
591 int pre_fork_channel_object(SPC_Channel_Ptr channel)
592 /*----------------------------------------------------------------------+*/
597 /* Set all wires to be "data ready" */
599 for(wirelist=channel->wire_list; wirelist; wirelist=wirelist->next) {
600 wirelist->flags |= SPCIO_DATA;
604 /* Move to the "Running & (possibly) data ready" state */
606 SPC_Change_State(channel, 0, flag, 1);
613 /*----------------------------------------------------------------------+*/
614 int post_fork_channel_object(SPC_Channel_Ptr UNUSED_PARM(channel),
615 int UNUSED_PARM(parentp))
616 /*----------------------------------------------------------------------+*/
622 /*----------------------------------------------------------------------+*/
623 int exec_proc_channel_object (SPC_Channel_Ptr channel)
624 /*----------------------------------------------------------------------+*/
628 int iomode=channel->IOMode;
630 /* If there is no argv specified, fix it up to be the convention
631 (argv[0] = file pathname) */
633 if (channel->argv == NULL) {
634 tmp_argv=Alloc_Argv(2);
635 if(tmp_argv==SPC_ERROR)
637 tmp_argv[0]=SPC_copy_string(channel->path);
639 channel->argv = tmp_argv;
640 channel->IOMode |= SPCIO_DEALLOC_ARGV;
643 if(IS_SPCIO_WAIT(channel->IOMode))
644 XeSPCRegisterTerminator(channel, NULL, NULL);
651 /*----------------------------------------------------------------------+*/
653 signal_channel_object (SPC_Channel_Ptr UNUSED_PARM(channel),
654 int UNUSED_PARM(sig))
655 /*----------------------------------------------------------------------+*/
661 /*----------------------------------------------------------------------+*/
662 int channel_object_wait_for_termination(SPC_Channel_Ptr UNUSED_PARM(channel))
663 /*----------------------------------------------------------------------+*/
669 /*----------------------------------------------------------------------+*/
671 attach_channel_object(SPC_Channel_Ptr UNUSED_PARM(channel),
672 int UNUSED_PARM(pid))
673 /*----------------------------------------------------------------------+*/
679 /*----------------------------------------------------------------------+*/
681 add_input_channel_object(SPC_Channel_Ptr UNUSED_PARM(channel),
682 SbInputHandlerProc UNUSED_PARM(handler),
683 void *UNUSED_PARM(data) )
684 /*----------------------------------------------------------------------+*/
690 /*----------------------------------------------------------------------+*/
692 remove_logfile_channel_object(SPC_Channel_Ptr channel)
693 /*----------------------------------------------------------------------+*/
695 if(IS_SPCIO_USE_LOGFILE(channel->IOMode))