Initial import of the CDE 2.1.30 sources from the Open Group.
[oweals/cde.git] / cde / lib / DtSvc / DtEncap / spc.c
1 /*
2  * File:         spc.c $XConsortium: spc.c /main/6 1996/06/21 17:33:08 ageorge $
3  * Language:     C
4  *
5  * (c) Copyright 1988, Hewlett-Packard Company, all rights reserved.
6  *
7  * (c) Copyright 1993, 1994 Hewlett-Packard Company                     *
8  * (c) Copyright 1993, 1994 International Business Machines Corp.       *
9  * (c) Copyright 1993, 1994 Sun Microsystems, Inc.                      *
10  * (c) Copyright 1993, 1994 Novell, Inc.                                *
11  */
12
13 #include <bms/sbport.h> /* NOTE: sbport.h must be the first include. */
14 #include <signal.h>
15 #include <SPC/spcP.h>
16 #include <SPC/spc-proto.h>
17 #include <bms/spc.h>
18 #include "DtSvcLock.h"
19
20 /* spc.c */
21 int SPC_Process_Single_Prot_Request (protocol_request_ptr req, SPC_Channel_Ptr channel);
22
23
24 /* This is the SPC error number variable */
25 /* extern int XeSPCErrorNumber; */
26
27 /* Externals */
28
29 extern int SPC_Initialized;
30 extern SPC_Channel_Ptr spc_activation_list;
31 extern SPC_Connection_Ptr connection_list;
32 extern SPC_Connection_Ptr read_terminator;
33 extern XeString official_hostname;
34
35 int max_fds = 0;   /* Set up below. */
36
37 /*----------------------------------------------------------------------+*/
38 void 
39 spc_init_fds(void)
40 /*----------------------------------------------------------------------+*/
41 {
42    _DtSvcProcessLock();
43    if (!max_fds)
44 #     ifdef __bsd
45          max_fds = getdtablesize();
46 #     else
47          max_fds = (int)sysconf(_SC_OPEN_MAX);
48 #     endif
49    _DtSvcProcessUnlock();
50 }
51
52 /*
53  ***
54  *** Sub-Process Control access routines.  These are just functional
55  *** interfaces to the underlying methods.
56  ***
57 */
58
59 /*----------------------------------------------------------------------+*/
60 SPC_Channel_Ptr XeSPCOpen(XeString hostname,
61                           int iomode)
62 /*----------------------------------------------------------------------+*/
63 {
64   /* Attempt to open an SPC channel - return TRUE if we succeed */
65   SPC_Channel_Ptr channel;
66
67   /* Check for initialization */
68   _DtSvcProcessLock();
69   if(!SPC_Initialized)
70       if(SPC_Initialize() == SPC_ERROR) {
71           _DtSvcProcessUnlock();
72           return(SPC_ERROR);
73       }
74   _DtSvcProcessUnlock();
75
76   /* The user specified iomode needs to be processed before we can use it.
77      Process the puppy. */
78
79   iomode=SPC_Transform_Iomode(iomode);
80   if(iomode==SPC_ERROR)
81     return(SPC_ERROR);
82        
83   /* Get a new channel object */
84   
85   channel=SPC_Initialize_Channel(hostname, iomode);
86
87   /* check that everything was okay */
88   
89   if(channel==SPC_ERROR)
90     return(SPC_ERROR);
91   
92   /* call the open method for it */
93
94   return((SPC_Channel_Ptr) mempf2(channel, open, iomode, hostname));
95
96 }
97
98 /*----------------------------------------------------------------------+*/
99 XeSPCClose(SPC_Channel_Ptr channel)
100 /*----------------------------------------------------------------------+*/
101 {
102   if(channel==SPC_ERROR) {
103     SPC_Error(SPC_Bad_Argument);
104     return(SPC_ERROR);
105   }
106
107   if(IS_ACTIVE(channel))
108     XeSPCKillProcess(channel, FALSE);
109
110   if(IS_SPCIO_DELAY_CLOSE(channel->IOMode)) {
111     channel->IOMode |= SPCIO_DO_CLOSE;
112     return(TRUE);
113   }
114   
115   channel->IOMode &= ~SPCIO_DO_CLOSE;
116   
117   return(mempf0(channel, close));
118   
119 }
120
121 /*----------------------------------------------------------------------+*/
122 XeSPCReset(SPC_Channel_Ptr channel)
123 /*----------------------------------------------------------------------+*/
124 {
125
126   if(channel==SPC_ERROR) {
127     SPC_Error(SPC_Bad_Argument);
128     return(SPC_ERROR);
129   }
130
131   if(IS_ACTIVE(channel))
132     XeSPCKillProcess(channel, FALSE);
133   
134   channel->IOMode &= ~SPCIO_ACTIVE;
135
136   if(mempf0(channel, reset)==SPC_ERROR)
137     return(SPC_ERROR);
138   
139   return(TRUE);
140   
141 }
142
143 /*----------------------------------------------------------------------+*/
144 XeSPCRead(SPC_Channel_Ptr channel,
145           int connector,           /* STDOUT or STDERR */
146           XeString buffer,
147           int length)
148 /*----------------------------------------------------------------------+*/
149 {
150
151   int n;
152   
153   /* check for legal arguments */
154   
155   if (channel==SPC_ERROR || !buffer ||
156       !(connector==STDOUT || connector==STDERR) ||
157       (length < 0)) {
158     SPC_Error(SPC_Bad_Argument);
159     return(SPC_ERROR);
160   }
161
162   /* check state of the channel */
163   if (!IS_DATA(channel) || !IS_SPCIO_DATA(channel->wires[connector]->flags))
164     return(0);
165   
166   /* call the read filter */
167
168   do {
169     n=(*channel->read_filter)(channel, connector, buffer, length);
170   } while(n == (EXCEPT_FLAG));
171   
172   /* Check for an error */
173
174   if (n == SPC_ERROR)
175     return(SPC_ERROR);
176   
177   return(n);
178 }
179
180   
181 /*----------------------------------------------------------------------+*/
182 XeSPCWrite(SPC_Channel_Ptr channel,
183            XeString buffer,
184            int length)
185 /*----------------------------------------------------------------------+*/
186 {
187   int n;
188   
189   /* check for legal arguments */
190   
191   if (channel==SPC_ERROR || !buffer || (length<0)) {
192     SPC_Error(SPC_Bad_Argument);
193     return(SPC_ERROR);
194   }
195
196   /* check the state of the channel */
197
198   if(!IS_ACTIVE(channel)) {
199     SPC_Error(SPC_Inactive_Channel);
200     return(SPC_ERROR);
201   }
202   
203   /* call the write method */
204   
205   n=mempf2(channel, write, buffer, length);
206   
207   return(n);
208 }
209
210 /*----------------------------------------------------------------------+*/
211 XeSPCActive(SPC_Channel_Ptr channel)
212 /*----------------------------------------------------------------------+*/
213 {
214
215   if (channel==SPC_ERROR) {
216     SPC_Error(SPC_Bad_Argument);
217     return(SPC_ERROR);
218   }
219   
220   /* Is the passed channel active? */
221   return (IS_ACTIVE(channel));
222 }
223
224 /*----------------------------------------------------------------------+*/
225 XeSPCData(SPC_Channel_Ptr channel)
226 /*----------------------------------------------------------------------+*/
227 {
228
229   if(channel==SPC_ERROR) {
230     SPC_Error(SPC_Bad_Argument);
231     return(SPC_ERROR);
232   }
233   
234   return(IS_DATA(channel));
235
236 }
237
238 /*----------------------------------------------------------------------+*/
239 XeSPCExecuteProcess(SPC_Channel_Ptr channel)
240 /*----------------------------------------------------------------------+*/
241 {
242   int retval;
243
244   if (channel==SPC_ERROR) {
245     SPC_Error(SPC_Bad_Argument);
246     return(SPC_ERROR);
247   }
248   
249   if(IS_ACTIVE(channel) || IS_DATA(channel)) {
250     SPC_Error(SPC_Active_Channel);
251     return(SPC_ERROR);
252   }
253
254   if((retval=mempf0(channel, exec_proc))==SPC_ERROR)
255     return(SPC_ERROR);
256
257   if (IS_SPCIO_WAIT(channel->IOMode)) {
258     /* Wait for sub-process to finish */
259     SPC_Wait_For_Termination(channel);
260   }
261   
262   return(retval);
263
264 }
265
266 /*----------------------------------------------------------------------+*/
267 XeSPCSignalProcess(SPC_Channel_Ptr channel,
268                    int sig)
269 /*----------------------------------------------------------------------+*/
270 {
271   
272   if ((channel==SPC_ERROR) ||
273       (channel->pid <= 0) ||
274       (sig < 0) 
275 #ifdef NOT_IN_XPG3_YET      
276       || (sig>=NSIG)    /* Not a good idea for interoperability anyway */
277 #endif
278       ) {
279     SPC_Error(SPC_Bad_Argument);
280     return(SPC_ERROR);
281   }
282
283   /* This routine does not check to see if the channel is active. */
284
285   return(mempf1(channel, signal, sig));
286 }
287
288 /*----------------------------------------------------------------------+*/
289 XeSPCAddInput(SPC_Channel_Ptr   channel,
290               SbInputHandlerProc handler,
291 /*----------------------------------------------------------------------+*/
292               void *            client_data)
293 {
294   if(!channel) {
295     SPC_Error(SPC_Bad_Argument);
296     return(SPC_ERROR);
297   }
298   
299   if(!handler && !channel->Input_Handler)
300     return(TRUE);
301
302   if(handler) {
303     channel->Input_Handler = handler;
304     channel->client_data   = client_data;
305   }
306
307   return(mempf2(channel, add_input, handler, client_data));
308     
309 }
310
311
312 /*----------------------------------------------------------------------+*/
313 XeSPCRegisterTerminator(SPC_Channel_Ptr                 channel,
314                         SPC_TerminateHandlerType        terminator,
315                         void *                          client_data)
316 /*----------------------------------------------------------------------+*/
317 {
318
319   SPC_Connection_Ptr conn;
320   
321   if(channel==SPC_ERROR) {
322     SPC_Error(SPC_Bad_Argument);
323     return(SPC_ERROR);
324   }
325
326   /* Okay.  If we have a pty channel, we have to check that we
327      have an input handler.  I don't like doing this here, but ther
328      are no methods for this routine, so it has to be done. */
329
330   if(IS_SPCIO_PTY(channel->IOMode) && !channel->Input_Handler) {
331     SPC_Error(SPC_Bad_Argument);
332     return(SPC_ERROR);
333   }
334
335   channel->IOMode |= SPCIO_SYNC_TERMINATOR;
336   if(terminator) {
337     channel->Terminate_Handler = terminator;
338     channel->Terminate_Data    = client_data;
339   }
340
341   conn=SPC_Channel_Terminator_Connection(channel);
342   if(conn->termination_id == -1)
343     SPC_XtAddInput(channel, &conn->termination_id, conn->sid,
344                    SPC_Conditional_Packet_Handler, SPC_Terminator);
345   
346   return(TRUE);
347   
348 }
349
350
351 /*----------------------------------------------------------------------+*/
352 XeSPCAttach(SPC_Channel_Ptr channel,
353             int pid)
354 /*----------------------------------------------------------------------+*/
355
356 {
357   if(channel==SPC_ERROR || pid<=0) {
358     SPC_Error(SPC_Bad_Argument);
359     return(SPC_ERROR);
360   }
361
362   return(mempf1(channel, attach, pid));
363 }
364
365 /*----------------------------------------------------------------------+*/
366 XeSPCDetach(SPC_Channel_Ptr channel)
367 /*----------------------------------------------------------------------+*/
368 {
369   if(channel == SPC_ERROR) {
370     SPC_Error(SPC_Bad_Argument);
371     return(SPC_ERROR);
372   }
373   
374   channel->pid = 0;
375   channel->IOMode &= ~SPCIO_ACTIVE;
376
377   return XeSPCReset(channel);
378 }
379   
380
381  
382 /*
383  ***
384  *** "Composite" functions -- Those which are defined in terms of the
385  *** above primitives.  One assumption is that the above primitives check
386  *** their arguments, so the composite functions need only check any
387  *** additional arguments.
388  ***
389 */
390
391 /*
392  **
393  ** Start with subprocess creation routines
394  **
395 */
396
397 /*----------------------------------------------------------------------+*/
398 XeSPCSpawn(XeString pathname,
399            XeString context_dir,
400            XeString *argv,
401            XeString *envp,
402            SPC_Channel_Ptr channel)
403 /*----------------------------------------------------------------------+*/
404 {
405
406   if(channel==SPC_ERROR) {
407     SPC_Error(SPC_Bad_Argument);
408     return(SPC_ERROR);
409   }
410   
411   /* Assign the command arguments to this channel and attempt to Execute */
412     
413   if(channel->envp) {
414     SPC_Free_Envp(channel->envp);
415     channel->envp=NULL;
416   }
417   if(channel->argv && IS_SPCIO_DEALLOC_ARGV(channel->IOMode)) {
418     SPC_Free_Envp(channel->argv);
419     channel->IOMode &= ~SPCIO_DEALLOC_ARGV;
420     channel->argv=NULL;
421   }
422   
423   channel->context_dir=context_dir;
424   channel->argv = argv;
425   channel->path = pathname;
426
427   channel->envp=SPC_Create_Default_Envp(channel->envp);
428   channel->envp=SPC_Merge_Envp(channel->envp, envp);
429   channel->envp=SPC_Fixup_Environment(channel->envp, channel);
430
431   if(IS_SPCIO_SYSTEM(channel->IOMode))
432     if(SPC_MakeSystemCommand(channel)==SPC_ERROR)
433       return(SPC_ERROR);
434   
435   /* Execute the process (XeSPCExecuteProcess will check arguments */
436   
437   return(XeSPCExecuteProcess(channel));
438 }
439
440 /*----------------------------------------------------------------------+*/
441 SPC_Channel_Ptr XeSPCOpenAndSpawn(XeString hostname,
442                                   int iomode,
443                                   XeString pathname,
444                                   XeString context_dir,
445                                   XeString *argv,
446                                   XeString *envp)
447 /*----------------------------------------------------------------------+*/
448 {
449   /* This simply wraps together the two steps: Open and Spawn */
450   SPC_Channel_Ptr channel;
451
452   channel = XeSPCOpen(hostname, iomode);
453   if(channel==SPC_ERROR)
454     return(SPC_ERROR);
455   
456   if (XeSPCSpawn(pathname, context_dir, argv, envp, channel)!=SPC_ERROR)
457     return(channel);
458
459   /* Close the channel and return SPC_ERROR */
460   XeSPCClose(channel);
461   return(SPC_ERROR);
462   
463 }
464
465 /*
466  **
467  ** Signalling routines
468  **
469 */
470
471 /*----------------------------------------------------------------------+*/
472 void
473 XeSPCKillProcesses(int wait)
474 /*----------------------------------------------------------------------+*/
475 {
476   /* Attempt to KILL all the sub-process that we know of */
477   SPC_Channel_Ptr spc;
478
479   _DtSvcProcessLock();
480   for (spc = spc_activation_list; spc != (SPC_Channel_Ptr) NULL; spc = spc->next)
481     XeSPCKillProcess(spc, wait);
482   _DtSvcProcessUnlock();
483 }
484
485 /*----------------------------------------------------------------------+*/
486 XeSPCKillProcess(SPC_Channel_Ptr channel,
487                  int wait)
488 /*----------------------------------------------------------------------+*/
489 {
490   /* Attempt to KILL the sub-process (should we nullify the pid?) */
491   int result;
492
493   if(!channel)
494     return(FALSE);
495
496   if(IS_ACTIVE(channel)) {
497     result = XeSPCSignalProcess(channel, SIGKILL);
498     if(result==SPC_ERROR)
499       return(SPC_ERROR);
500     if (wait || IS_SPCIO_SYNC_TERM(channel->IOMode))
501       SPC_Wait_For_Termination(channel);
502     return result;
503   } else
504     return(TRUE);
505 }
506
507 /*----------------------------------------------------------------------+*/
508 XeSPCInterruptProcess(SPC_Channel_Ptr channel)
509 /*----------------------------------------------------------------------+*/
510 {
511   /* Attempt to INTerrupt the sub-process */
512   
513   return(XeSPCSignalProcess(channel, SIGINT));
514 }
515
516 /*
517  **
518  ** Process information routines.
519  **
520 */
521
522 /*----------------------------------------------------------------------+*/
523 XeString XeSPCGetDevice(SPC_Channel_Ptr channel,
524                               int connector, 
525                               int side)
526 /*----------------------------------------------------------------------+*/
527 {
528   if(!channel) {
529     SPC_Error(SPC_Bad_Argument);
530     return(SPC_ERROR);
531   }
532   
533   /* Return the device name which corresponds to the side of a channel */
534   if (connector>=STDIN && connector<=STDERR) {
535     if (side == MASTER_SIDE)
536       return(channel->wires[connector]->master_name);
537     if (side == SLAVE_SIDE)
538       return(channel->wires[connector]->slave_name);
539   }
540   
541   /* For no channel or incorrect side, return SPC_ERROR */
542   
543   SPC_Error(SPC_Bad_Argument);
544   return(SPC_ERROR);
545 }
546
547 /*----------------------------------------------------------------------+*/
548 XeSPCGetProcessStatus(SPC_Channel_Ptr channel,
549                       int *type, 
550                       int *cause)
551 /*----------------------------------------------------------------------+*/
552 {
553   /* Fill in the type and cause of a process termination */
554   int high, low;
555
556   if(!channel || !type || !cause) {
557     SPC_Error(SPC_Bad_Argument);
558     return(SPC_ERROR);
559   }
560     
561   low = channel->status & WAIT_STATUS_MASK;
562   high = (channel->status >> 8) & WAIT_STATUS_MASK;
563
564   *cause = high;
565
566   switch (low) {
567
568   case IS_WAIT_STATUS_STOPPED:
569     *type = SPC_PROCESS_STOPPED;
570     break;
571
572   case IS_WAIT_STATUS_EXITED:
573     *type = SPC_PROCESS_EXITED;
574     break;
575
576   default:
577     if (!*cause) {
578       *cause = low;
579       *type = SPC_PROCESS_SIGNALLED;
580     }
581     break;
582
583   }                             /* End switch on status */
584
585   /* When a process is still active return FALSE */
586   return(TRUE);
587 }
588
589 /*----------------------------------------------------------------------+*/
590 XeSPCGetPID(SPC_Channel_Ptr channel)
591 /*----------------------------------------------------------------------+*/
592 {
593
594   if(!channel) {
595     SPC_Error(SPC_Bad_Argument);
596     return(SPC_ERROR);
597   }
598   return (channel->pid);
599 }
600
601 /*----------------------------------------------------------------------+*/
602 int XeSPCGetLogfile(SPC_Channel_Ptr channel,
603                     XeString *host, 
604                     XeString *file)
605 /*----------------------------------------------------------------------+*/
606 {
607
608   if(!channel || !IS_SPCIO_USE_LOGFILE(channel->IOMode)) {
609     SPC_Error(SPC_Bad_Argument);
610     return(SPC_ERROR);
611   }
612   *file=channel->logfile;
613   _DtSvcProcessLock();
614   if(IS_REMOTE(channel))
615     *host=channel->connection->hostname;
616   else
617     *host= official_hostname;
618   _DtSvcProcessUnlock();
619   return(TRUE);
620   
621 }
622
623 /*----------------------------------------------------------------------+*/
624 int XeSPCRemoveLogfile(SPC_Channel_Ptr channel)
625 /*----------------------------------------------------------------------+*/
626
627
628   if(!channel) {
629     SPC_Error(SPC_Bad_Argument);
630     return(SPC_ERROR);
631   }
632   
633   return(mempf0(channel, remove_logfile));
634 }
635
636 /*
637  **
638  ** Synchronous termination
639  **
640 */
641
642 #define SINGLE_PROT_DATA(req, channel, connector)  \
643   if(!channel->queued_remote_data)                 \
644     channel->queued_remote_data=Xe_make_queue(FALSE); \
645   Xe_push_queue(channel->queued_remote_data, req);    \
646   if(channel->Input_Handler) {                     \
647     SPC_Input_Handler(channel, connector);         \
648   }
649
650 /*
651  **
652  ** SPC_Process_Single_Prot_Request will return TRUE if it is okay
653  ** for the caller to free the protocol request.
654  **
655 */
656
657 /*----------------------------------------------------------------------+*/
658 SPC_Process_Single_Prot_Request(protocol_request_ptr req, SPC_Channel_Ptr channel)
659 /*----------------------------------------------------------------------+*/
660 {
661
662   switch(req->request_type) {
663     
664   case APPLICATION_DIED:
665     
666     READ_APPLICATION_DIED(req->dataptr, channel->status);
667     SPC_Channel_Terminated(channel);
668     return(TRUE);
669     
670   case APPLICATION_STDOUT:
671     SINGLE_PROT_DATA(req, channel, STDOUT);
672     return(FALSE);
673     
674   case APPLICATION_STDERR:
675     SINGLE_PROT_DATA(req, channel, STDERR);
676     return(FALSE);
677       
678   default:
679     SPC_Error(SPC_Internal_Error);
680     return(SPC_ERROR);
681   }
682 }    
683   
684 /*----------------------------------------------------------------------+*/
685 SPC_Channel_Ptr XeSPCHandleTerminator(int fd)
686 /*----------------------------------------------------------------------+*/
687 {
688   SPC_Connection_Ptr connection;
689   SPC_Channel_Ptr channel;
690   protocol_request_ptr prot;
691   XeQueue connection_queue;
692   
693   if(!(connection=SPC_Lookup_Connection_Fd(fd))) {
694     SPC_Error(SPC_Bad_Argument);
695     return(SPC_ERROR);
696   }
697   
698   if(!(prot=SPC_Read_Protocol(connection))) {
699     return(SPC_ERROR);
700   }
701
702   connection_queue=connection->queued_remote_data;
703   Xe_push_queue(connection_queue, prot);
704
705   while(prot=(protocol_request_ptr)Xe_pop_queue(connection_queue)) {
706     
707     channel=prot->channel;
708
709     if(channel) {
710       channel->IOMode |= SPCIO_DELAY_CLOSE;
711       
712       if(SPC_Process_Single_Prot_Request(prot, channel))
713         SPC_Free_Protocol_Ptr(prot);
714       
715       channel->IOMode &= ~SPCIO_DELAY_CLOSE;
716       if(IS_SPCIO_DO_CLOSE(channel->IOMode)) {
717         XeSPCClose(channel);
718         channel = NULL;
719       }
720     }
721     
722   }
723   
724   return(channel);
725   
726 }
727
728 /*
729  **
730  ** Use this call to get the file descriptor for
731  ** Synchronous termination.
732  **
733 */
734
735 /*----------------------------------------------------------------------+*/
736 XeSPCGetChannelSyncFd(SPC_Channel_Ptr channel)
737 /*----------------------------------------------------------------------+*/
738 {
739
740   SPC_Connection_Ptr conn;
741
742   if(channel==SPC_ERROR) {
743     SPC_Error(SPC_Bad_Argument);
744     return(SPC_ERROR);
745   }
746   
747   conn=SPC_Channel_Terminator_Connection(channel);
748   return(conn->sid);
749   
750 }
751
752 /*
753  ***
754  *** Error Handling routines.
755  ***
756 */
757
758 /*----------------------------------------------------------------------+*/
759 SPCError *XeSPCLookupError(int errnum)
760 /*----------------------------------------------------------------------+*/
761 {
762   if(errnum<SPC_Min_Error || errnum > SPC_Max_Error) {
763     SPC_Error(SPC_Bad_Argument);
764     return(SPC_ERROR); 
765   }
766    
767   return(SPC_Lookup_Error(errnum));
768 }
769
770 /*
771  ***
772  *** Temporarily shutdown input handlers
773  ***
774 */
775
776 /*----------------------------------------------------------------------+*/
777 void XeSPCShutdownCallbacks(void)
778 /*----------------------------------------------------------------------+*/
779 {
780   SPC_Channel_Ptr channel;
781   SPC_Connection_Ptr conn;
782   Wire *wirelist;
783
784   _DtSvcProcessLock();
785   channel=spc_activation_list;
786   conn=connection_list;
787
788   while(channel) {
789     for(wirelist=channel->wire_list; wirelist; wirelist=wirelist->next) {
790       if(wirelist->read_toolkit_id   != -1)
791         SPC_XtRemoveInput(&wirelist->read_toolkit_id, SPC_Input);
792       if(wirelist->except_toolkit_id != -1)
793         SPC_XtRemoveInput(&wirelist->except_toolkit_id, SPC_Exception);
794     }
795     channel=channel->next;
796   }
797
798   while(conn) {
799     if(conn->termination_id != -1)
800       SPC_XtRemoveInput(&conn->termination_id, SPC_Terminator);
801     conn=conn->next;
802   }
803   _DtSvcProcessUnlock();
804 }
805
806 /*----------------------------------------------------------------------+*/
807 void XeSPCRestartCallbacks(void)
808 /*----------------------------------------------------------------------+*/
809 {
810   SPC_Channel_Ptr channel;
811
812   _DtSvcProcessLock();
813   channel=spc_activation_list;
814
815   while(channel) {
816     if(channel->Input_Handler)
817       XeSPCAddInput(channel, (SbInputHandlerProc)NULL, NULL);
818     if(channel->Terminate_Handler)
819       XeSPCRegisterTerminator(channel, NULL, NULL);
820     channel=channel->next;
821   }
822   _DtSvcProcessUnlock();
823 }
824
825 /*
826  ***
827  *** Okay, now for a non-SPC routine.  This one is dealing with setpgrp,
828  *** but it is here because it uses internal SPC routines.
829  ***
830 */
831
832 /*----------------------------------------------------------------------+*/
833 XeSetpgrp(int read_current_termio)
834 /*----------------------------------------------------------------------+*/
835 {
836
837   return(SPC_Setpgrp(read_current_termio));
838 }  
839
840 int XeSPCSendEOF(SPC_Channel_Ptr channel)
841 {
842   if(channel==SPC_ERROR) {
843     SPC_Error(SPC_Bad_Argument);
844     return(SPC_ERROR);
845   }
846   
847   return(mempf0(channel, send_eof));
848 }
849
850 int XeSPCSetTermio(SPC_Channel_Ptr channel, int connection, int side,
851                    struct termios *termio)
852 {
853   if(channel==SPC_ERROR || termio == NULL) {
854     SPC_Error(SPC_Bad_Argument);
855     return(SPC_ERROR);
856   }
857   
858   return(mempf3(channel, set_termio, connection, side, termio));
859 }
860