Convert uses of XKeycodeToKeysym (deprecated) to XkbKeycodeToKeysym
[oweals/cde.git] / cde / lib / DtSvc / DtEncap / spc-obj.c
1 /*
2  * CDE - Common Desktop Environment
3  *
4  * Copyright (c) 1993-2012, The Open Group. All rights reserved.
5  *
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)
10  * any later version.
11  *
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
16  * details.
17  *
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
22  */
23 /*
24  * File:         spc-obj.c $TOG: spc-obj.c /main/6 1997/12/29 10:43:29 bill $
25  * Language:     C
26  *
27  * (c) Copyright 1989, Hewlett-Packard Company, all rights reserved.
28  *
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.                                *
33  */
34
35 #include <bms/sbport.h> /* NOTE: sbport.h must be the first include. */
36 #include <signal.h>
37
38 #include <SPC/spcP.h>
39 #include <bms/MemoryMgr.h>
40
41 #include <SPC/spc-proto.h>
42 #include "DtSvcLock.h"
43
44 /* global declarations */
45
46 /* SPC_Initialized is in bmsglob.c */
47
48 extern int SPC_Initialized;
49
50 /* external declarations */
51
52 extern SPC_Channel_Ptr spc_activation_list;
53 extern XeString spc_user_environment_file;
54
55 /*
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
60  * be installed.
61  *
62  * This will be set to 'SPC_I_AM_A_DAEMON' by the spcd process.
63  */
64 int SPC_who_am_i = SPC_I_AM_A_CLIENT;
65
66 /* Initialization functions for class objects */
67
68
69 /*----------------------------------------------------------------------+*/
70 object *alloc_channel_object(object_clasp c)
71 /*----------------------------------------------------------------------+*/
72 {
73   object *p=(object *) XeMalloc((unsigned) c->object_size);
74   memset(p, 0, (int) c->object_size);
75   return(p);
76 }
77
78 /*----------------------------------------------------------------------+*/
79 void channel_class_init(object_clasp t)
80 /*----------------------------------------------------------------------+*/
81 {
82
83   channel_clasp c = (channel_clasp) t;
84   
85   c->new_obj    = alloc_channel_object;
86   
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;
98   c->input      = NULL;
99   c->add_input  = add_input_channel_object;
100   c->remove_logfile = remove_logfile_channel_object;
101 }
102
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 */
108   0
109   };
110
111 channel_clasp channel_class = &channel_class_struct;
112
113 static Wire dummy_wire={
114   0,                             /* Flags */
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 */
120   };
121
122 /*----------------------------------------------------------------------+*/
123 int
124 SPC_ResetTerminator(void)
125 /*----------------------------------------------------------------------+*/
126 {
127   struct sigaction svect;
128
129   _DtSvcProcessLock();
130   if (SPC_who_am_i == SPC_I_AM_A_DAEMON) {
131     svect.sa_handler = SPC_Child_Terminated;
132     sigemptyset(&svect.sa_mask);
133     svect.sa_flags = 0;
134
135     if(sigaction(SIGCHLD, &svect, (struct sigaction *)NULL)==ERROR) {
136       SPC_Error(SPC_No_Signal_Handler);
137       return(SPC_ERROR);
138     }
139   }
140
141   _DtSvcProcessUnlock();
142   return (TRUE);
143 }
144
145 /*----------------------------------------------------------------------+*/
146 int
147 SPC_Initialize(void)
148 /*----------------------------------------------------------------------+*/
149 {
150
151   XeString home;
152
153   _DtSvcProcessLock();
154   if(SPC_Initialized) {
155      _DtSvcProcessUnlock();
156      return(TRUE);
157   }
158
159   spc_init_fds();
160
161   if (!SPC_ResetTerminator()) {
162      _DtSvcProcessUnlock();
163      return(SPC_ERROR);
164   }
165     
166   if(!SPC_Init_Local_Host_Info()) {
167      _DtSvcProcessUnlock();
168      return(SPC_ERROR);
169   }
170
171   if(SPC_Setup_Synchronous_Terminator()==SPC_ERROR) {
172      _DtSvcProcessUnlock();
173      return(SPC_ERROR);
174   }
175
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);
181   }
182   
183   SPC_Initialized=TRUE;
184   _DtSvcProcessUnlock();
185
186   return(TRUE);
187 }
188
189 /*
190  **
191  ** SPC_Initialize_Channel will create & return a channel object,
192  ** based on the values of hostname and iomode.
193  **
194 */
195
196 /*----------------------------------------------------------------------+*/
197 SPC_Channel_Ptr SPC_Initialize_Channel(XeString hostname,
198                                        int iomode)
199 /*----------------------------------------------------------------------+*/
200 {
201   SPC_Channel_Ptr channel = NULL;
202   
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);
207
208   } else {
209
210     /* We are local.  Create the appropriate object. */
211     
212     if(IS_SPCIO_NOIOMODE(iomode))
213       channel=(SPC_Channel_Ptr)object_create((object_clasp)noio_channel_class);
214     
215     if(IS_SPCIO_PIPE(iomode))
216       channel=(SPC_Channel_Ptr)object_create((object_clasp)pipe_channel_class);
217     
218     if(IS_SPCIO_PTY(iomode))
219       channel=(SPC_Channel_Ptr)object_create((object_clasp)pty_channel_class);
220
221   }
222
223   return(channel);
224 }
225
226 /*
227  **
228  ** SPC_Channel_Terminated will do any work necessary on a channel when 
229  ** we detect that a subprocess has terminated.
230  **
231 */
232
233 /*----------------------------------------------------------------------+*/
234 void SPC_Channel_Terminated(SPC_Channel_Ptr channel)
235 /*----------------------------------------------------------------------+*/
236 {
237   int type, cause;
238
239   SPC_Change_State(channel, 0, -1, 0);
240
241   /* Set the close timeout.  If we are on a PTY, we will return
242      after two seconds if we are waiting for EOF */
243   
244   channel->close_timeout=2;
245
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);
251   }
252   
253   if(channel->Terminate_Handler) {
254     XeSPCGetProcessStatus(channel, &type, &cause);
255     (* channel->Terminate_Handler)
256       (channel, channel->pid, type, cause, channel->Terminate_Data);
257   }
258       
259   channel->close_timeout=0;
260   
261 }
262
263 /*
264  **
265  ** SPC_Check style makes sure that we have a legal IOMode.
266  **
267 */
268
269 /*----------------------------------------------------------------------+*/
270 int
271 SPC_Check_Style(int iomode)
272 /*----------------------------------------------------------------------+*/
273 {
274
275   int stylecount=0;
276   
277   /* First, make sure that we have only one style bit set */
278
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 */
281
282   if(IS_SPCIO_NOIOMODE(iomode))
283     stylecount++;
284   if(IS_SPCIO_PIPE(iomode))
285     stylecount++;
286   if(IS_SPCIO_PTY(iomode))
287     stylecount++;
288
289   if(stylecount != 1) {
290     SPC_Error(SPC_Illegal_Iomode);
291     return(SPC_ERROR);
292   }
293
294   /* Okay, now check to make sure we don't have any conflicting
295      modes set */
296   
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))
301       )
302     {
303       SPC_Error(SPC_Illegal_Iomode);
304       return(SPC_ERROR);
305     }
306   
307   return(TRUE);
308
309 }
310
311 /*
312  **
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.
316  **
317 */
318
319 /*----------------------------------------------------------------------+*/
320 int
321 SPC_Transform_Iomode(int iomode)
322 /*----------------------------------------------------------------------+*/
323 {
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;
330
331   /* Check to make sure that the iomode is consistent */
332   
333   if(SPC_Check_Style(iomode)==SPC_ERROR)
334     return(SPC_ERROR);
335
336   return(iomode);
337
338 }
339
340 /*
341  **
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.
345  **
346 */
347
348 /*----------------------------------------------------------------------+*/
349 int
350 SPC_Newline_Filter(SPC_Channel_Ptr channel,
351                    int connector, 
352                    XeString buffer,
353                    int ntoread)
354 /*----------------------------------------------------------------------+*/
355 {
356   buffered_data_ptr cbuf;
357   XeString usrptr;
358   XeString cbufptr;
359   int nchars, nlcopied, scalarlen, nchars_this_buffer;
360
361   if(!(cbuf=channel->linebufs[connector])) {
362     if((cbuf=SPC_New_Buffered_Data_Ptr())==SPC_ERROR)
363       return(SPC_ERROR);
364     channel->linebufs[connector]=cbuf;
365   }
366
367   usrptr=buffer;
368   cbufptr=cbuf->data+cbuf->offset;
369   nchars=0;
370   nlcopied = FALSE;
371   channel->IOMode &= ~SPCIO_HAS_DATA;
372   
373   do {
374
375     nchars_this_buffer=0;
376     scalarlen=cbuf->len;
377
378     while(nchars<ntoread && nchars_this_buffer<scalarlen && !nlcopied) {
379       nlcopied = (*cbufptr == Newline);
380       *usrptr++ = (*cbufptr++);
381       nchars++;
382       nchars_this_buffer++;
383     }
384
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;
390       return(nchars);
391     }
392
393     cbufptr = cbuf->data;
394     cbuf->offset = 0;
395     do {
396       cbuf->len=mempf3(channel, read, connector, cbufptr, SPC_BUFSIZ);
397     } while(cbuf->len == (EXCEPT_FLAG));
398
399     cbufptr[cbuf->len]=0;
400     
401   } while((cbuf->len) > 0);
402
403   return(nchars);
404 }
405
406 /*----------------------------------------------------------------------+*/
407 int
408 SPC_Input_Handler(SPC_Channel_Ptr channel,
409                   int connector)
410 /*----------------------------------------------------------------------+*/
411 {
412
413   int nchars;
414   XeChar spc_iobuffer[SPC_BUFSIZ+1];
415
416   channel->IOMode &= ~SPCIO_HAS_DATA;
417
418   do {
419     
420     nchars=(*channel->read_filter)
421       (channel, connector, spc_iobuffer, SPC_BUFSIZ);
422     
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 */
425     
426     if(nchars==EXCEPT_FLAG)
427       return(FALSE);
428
429     /* Call Read handlers */
430     
431     spc_iobuffer[nchars]=XeChar_NULL;
432     
433     if(channel->Input_Handler)
434       (* channel->Input_Handler)
435         (channel->client_data, spc_iobuffer, nchars, connector);
436     
437   } while(HAS_DATA(channel));
438   
439   return(nchars);
440 }
441
442 /*
443  ***
444  *** Method definitions for channel objects
445  ***
446 */
447
448 /*
449  * This routine handles initialization which must occur for every channel.
450  */
451
452 /*----------------------------------------------------------------------+*/
453 SPC_Channel_Ptr open_channel_object(SPC_Channel_Ptr channel,
454                                     int iomode,
455                                     XeString UNUSED_PARM(hostname))
456 /*----------------------------------------------------------------------+*/
457
458 {
459
460   /* initialize local data structures */
461
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
464      method. */
465      
466   if(IS_SPCIO_LINEORIENTED(iomode))
467     channel->read_filter=SPC_Newline_Filter;
468   else
469     channel->read_filter=channel->class_ptr->read;
470
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;
483   
484   /* Link it into the activation list (at the front for now) */
485
486   channel->next = spc_activation_list;
487   spc_activation_list = channel;
488   
489   return(channel);
490 }
491
492 /*
493  **
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
496  **
497 */
498
499 /*----------------------------------------------------------------------+*/
500 int close_channel_object (SPC_Channel_Ptr channel)
501 /*----------------------------------------------------------------------+*/
502 {
503   Wire *wirelist, *next_wire;
504   int i;
505   SPC_Channel_Ptr trail, ptr;
506   
507   /* Remove the channel from the activation list */
508
509   if(spc_activation_list == channel)
510     spc_activation_list = channel->next;
511   else {
512     trail = spc_activation_list;
513     while(trail) {
514       ptr = trail->next;
515       if(ptr == channel) {
516         trail->next = ptr->next;
517         break;
518       }
519       trail=ptr;
520     }
521     if(!trail) {
522       SPC_Error(SPC_Closed_Channel);
523       return(SPC_ERROR);
524     }
525   }
526   
527   /* Deallocate any memory allocated to the subfields */
528   
529   if(IS_SPCIO_DEALLOC_ARGV(channel->IOMode))
530     SPC_Free_Envp(channel->argv);
531   SPC_Free_Envp(channel->envp);
532   
533   wirelist=channel->wire_list;
534   while(wirelist) {
535     next_wire=wirelist->next;
536     free_wire(wirelist);
537     wirelist=next_wire;
538   }
539
540   for(i=1; i<3; i++)
541     if(channel->linebufs[i])
542       free((char *)channel->linebufs[i]);
543
544   /* Free the queue associated with the channel */
545   
546   SPC_Flush_Queued_Data(channel);
547   Xe_release_queue(channel->queued_remote_data);
548   
549   /* Deallocate the channel */
550
551   free((char *)channel);
552   
553   return(TRUE);
554 }
555
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 /*----------------------------------------------------------------------+*/
562   
563 {
564   /* need to check consistency between connector and READ/WRITE/ERROR here */
565   return(TRUE);
566 }
567
568
569 /*----------------------------------------------------------------------+*/
570 int write_channel_object(SPC_Channel_Ptr UNUSED_PARM(channel),
571                          XeString        UNUSED_PARM(buffer),
572                          int             UNUSED_PARM(nbytes))
573 /*----------------------------------------------------------------------+*/
574   
575 {
576   /* check for consistent arguments (channel open for WRITE) */
577   return(TRUE);
578 }
579
580
581 /*----------------------------------------------------------------------+*/
582 int reset_channel_object(SPC_Channel_Ptr channel)
583 /*----------------------------------------------------------------------+*/
584 {
585   channel->IOMode &= ~SPCIO_DATA;
586   return(TRUE);
587 }
588
589
590 /*----------------------------------------------------------------------+*/
591 int pre_fork_channel_object(SPC_Channel_Ptr channel)
592 /*----------------------------------------------------------------------+*/
593 {
594   Wire *wirelist;
595   int  flag=0;
596   
597   /* Set all wires to be "data ready" */
598   
599   for(wirelist=channel->wire_list; wirelist; wirelist=wirelist->next) {
600     wirelist->flags |= SPCIO_DATA;
601     flag=1;
602   }
603
604   /* Move to the "Running & (possibly) data ready" state */
605   
606   SPC_Change_State(channel, 0, flag, 1);
607
608   
609   return(TRUE);
610 }
611
612
613 /*----------------------------------------------------------------------+*/
614 int post_fork_channel_object(SPC_Channel_Ptr UNUSED_PARM(channel),
615                              int             UNUSED_PARM(parentp))
616 /*----------------------------------------------------------------------+*/
617 {
618   return(TRUE);
619 }
620
621
622 /*----------------------------------------------------------------------+*/
623 int  exec_proc_channel_object (SPC_Channel_Ptr channel)
624 /*----------------------------------------------------------------------+*/
625 {
626
627   XeString *tmp_argv;
628   int iomode=channel->IOMode;
629   
630   /* If there is no argv specified, fix it up to be the convention
631      (argv[0] = file pathname) */
632   
633   if (channel->argv == NULL) {
634     tmp_argv=Alloc_Argv(2);
635     if(tmp_argv==SPC_ERROR)
636       return(SPC_ERROR);
637     tmp_argv[0]=SPC_copy_string(channel->path);
638     tmp_argv[1]=NULL;
639     channel->argv = tmp_argv;
640     channel->IOMode |= SPCIO_DEALLOC_ARGV;
641   }
642
643   if(IS_SPCIO_WAIT(channel->IOMode))
644     XeSPCRegisterTerminator(channel, NULL, NULL);
645   
646   return(TRUE);
647
648 }
649
650
651 /*----------------------------------------------------------------------+*/
652 int
653 signal_channel_object (SPC_Channel_Ptr UNUSED_PARM(channel),
654                        int             UNUSED_PARM(sig))
655 /*----------------------------------------------------------------------+*/
656 {
657   return(TRUE);
658 }
659
660
661 /*----------------------------------------------------------------------+*/
662 int channel_object_wait_for_termination(SPC_Channel_Ptr UNUSED_PARM(channel))
663 /*----------------------------------------------------------------------+*/
664 {
665   return(TRUE);
666 }
667
668
669 /*----------------------------------------------------------------------+*/
670 int
671 attach_channel_object(SPC_Channel_Ptr UNUSED_PARM(channel),
672                       int             UNUSED_PARM(pid))
673 /*----------------------------------------------------------------------+*/
674 {
675   return(TRUE);
676 }
677
678
679 /*----------------------------------------------------------------------+*/
680 int
681 add_input_channel_object(SPC_Channel_Ptr    UNUSED_PARM(channel), 
682                          SbInputHandlerProc UNUSED_PARM(handler),
683                          void              *UNUSED_PARM(data) )
684 /*----------------------------------------------------------------------+*/
685 {
686   return(TRUE);
687 }
688
689
690 /*----------------------------------------------------------------------+*/
691 int
692 remove_logfile_channel_object(SPC_Channel_Ptr channel)
693 /*----------------------------------------------------------------------+*/
694 {
695   if(IS_SPCIO_USE_LOGFILE(channel->IOMode))
696     return(TRUE);
697   else
698     return(FALSE);
699 }