XEmacs 21.2.43 "Terspichore".
[chise/xemacs-chise.git.1] / lib-src / gnuserv.c
1 /* -*-C-*-
2  Server code for handling requests from clients and forwarding them
3  on to the GNU Emacs process.
4
5  This file is part of GNU Emacs.
6
7  Copying is permitted under those conditions described by the GNU
8  General Public License.
9
10  Copyright (C) 1989 Free Software Foundation, Inc.
11
12  Author: Andy Norman (ange@hplb.hpl.hp.com), based on 'etc/server.c'
13          from the 18.52 GNU Emacs distribution.
14
15  Please mail bugs and suggestions to the author at the above address.
16 */
17
18 /* HISTORY
19  * 11-Nov-1990          bristor@simba
20  *    Added EOT stuff.
21  */
22
23 /*
24  * This file incorporates new features added by Bob Weiner <weiner@mot.com>,
25  * Darrell Kindred <dkindred@cmu.edu> and Arup Mukherjee <arup@cmu.edu>.
26  * Please see the note at the end of the README file for details.
27  *
28  * (If gnuserv came bundled with your emacs, the README file is probably
29  * ../etc/gnuserv.README relative to the directory containing this file)
30  */
31
32 #include "gnuserv.h"
33
34 char gnuserv_version[] = "gnuserv version" GNUSERV_VERSION;
35
36
37 #ifdef USE_LITOUT
38 #ifdef linux
39 #include <bsd/sgtty.h>
40 #else
41 #include <sgtty.h>
42 #endif
43 #endif
44
45 #ifdef AIX
46 #include <sys/select.h>
47 #endif
48
49 #include <stdlib.h>
50 #include <stdio.h>
51 #include <sys/types.h>
52 #include <sys/stat.h>
53
54 #ifdef HAVE_UNISTD_H
55 #include <unistd.h>
56 #endif /* HAVE_UNISTD_H */
57
58 #ifdef HAVE_STRING_H
59 #include <string.h>
60 #endif /* HAVE_STRING_H */
61
62 #if !defined(SYSV_IPC) && !defined(UNIX_DOMAIN_SOCKETS) && \
63     !defined(INTERNET_DOMAIN_SOCKETS)
64 main ()
65 {
66   fprintf (stderr,"Sorry, the Emacs server is only supported on systems that have\n");
67   fprintf (stderr,"Unix Domain sockets, Internet Domain sockets or System V IPC\n");
68   exit (1);
69 } /* main */
70 #else /* SYSV_IPC || UNIX_DOMAIN_SOCKETS || INTERNET_DOMAIN_SOCKETS */
71
72 #ifdef SYSV_IPC
73
74 int ipc_qid = 0;                /* ipc message queue id */
75 pid_t ipc_wpid = 0;             /* watchdog task pid */
76
77
78 /*
79   ipc_exit -- clean up the queue id and queue, then kill the watchdog task
80               if it exists. exit with the given status.
81 */
82 void
83 ipc_exit (int stat)
84 {
85   msgctl (ipc_qid,IPC_RMID,0);
86
87   if  (ipc_wpid != 0)
88     kill (ipc_wpid, SIGKILL);
89
90   exit (stat);
91 } /* ipc_exit */
92
93
94 /*
95   ipc_handle_signal -- catch the signal given and clean up.
96 */
97 void
98 ipc_handle_signal(int sig)
99 {
100   ipc_exit (0);
101 } /* ipc_handle_signal */
102
103
104 /*
105   ipc_spawn_watchdog -- spawn a watchdog task to clean up the message queue should the
106                         server process die.
107 */
108 void
109 ipc_spawn_watchdog (void)
110 {
111   if ((ipc_wpid = fork ()) == 0)
112     { /* child process */
113       pid_t ppid = getppid ();  /* parent's process id */
114
115       setpgrp();                /* gnu kills process group on exit */
116
117       while (1)
118         {
119           if (kill (ppid, 0) < 0) /* ppid is no longer valid, parent
120                                      may have died */
121             {
122               ipc_exit (0);
123             } /* if */
124
125           sleep(10);            /* have another go later */
126         } /* while */
127     } /* if */
128
129 } /* ipc_spawn_watchdog */
130
131
132 /*
133   ipc_init -- initialize server, setting the global msqid that can be listened on.
134 */
135 void
136 ipc_init (struct msgbuf **msgpp)
137 {
138   key_t key;                    /* messge key */
139   char buf[GSERV_BUFSZ];        /* pathname for key */
140
141   sprintf (buf,"%s/gsrv%d",tmpdir,(int)geteuid ());
142   creat (buf,0600);
143   key = ftok (buf,1);
144
145   if ((ipc_qid = msgget (key,0600|IPC_CREAT)) == -1)
146     {
147       perror (progname);
148       fprintf (stderr, "%s: unable to create msg queue\n", progname);
149       ipc_exit (1);
150     } /* if */
151
152   ipc_spawn_watchdog ();
153
154   signal (SIGTERM,ipc_handle_signal);
155   signal (SIGINT,ipc_handle_signal);
156
157   if ((*msgpp = (struct msgbuf *)
158        malloc (sizeof **msgpp + GSERV_BUFSZ)) == NULL)
159     {
160       fprintf (stderr,
161                "%s: unable to allocate space for message buffer\n", progname);
162       ipc_exit(1);
163     } /* if */
164 } /* ipc_init */
165
166
167 /*
168   handle_ipc_request -- accept a request from a client, pass the request on
169                         to the GNU Emacs process, then wait for its reply and
170                         pass that on to the client.
171 */
172 void
173 handle_ipc_request (struct msgbuf *msgp)
174 {
175   struct msqid_ds msg_st;       /* message status */
176   char buf[GSERV_BUFSZ];
177   int len;                      /* length of message / read */
178   int s, result_len;            /* tag fields on the response from emacs */
179   int offset = 0;
180   int total = 1;                /* # bytes that will actually be sent off */
181
182   if ((len = msgrcv (ipc_qid, msgp, GSERV_BUFSZ - 1, 1, 0)) < 0)
183     {
184       perror (progname);
185       fprintf (stderr, "%s: unable to receive\n", progname);
186       ipc_exit (1);
187     } /* if */
188
189   msgctl (ipc_qid, IPC_STAT, &msg_st);
190   strncpy (buf, msgp->mtext, len);
191   buf[len] = '\0';              /* terminate */
192
193   printf ("%d %s", ipc_qid, buf);
194   fflush (stdout);
195
196   /* now for the response from gnu */
197   msgp->mtext[0] = '\0';
198
199 #if 0
200   if ((len = read(0,buf,GSERV_BUFSZ-1)) < 0)
201     {
202       perror (progname);
203       fprintf (stderr, "%s: unable to read\n", progname);
204       ipc_exit (1);
205   } /* if */
206
207   sscanf (buf, "%d:%[^\n]\n", &junk, msgp->mtext);
208 #else
209
210   /* read in "n/m:" (n=client fd, m=message length) */
211
212   while (offset < (GSERV_BUFSZ-1) &&
213          ((len = read (0, buf + offset, 1)) > 0) &&
214          buf[offset] != ':')
215     {
216       offset += len;
217     }
218
219   if (len < 0)
220     {
221       perror (progname);
222       fprintf (stderr, "%s: unable to read\n", progname);
223       exit(1);
224     }
225
226   /* parse the response from emacs, getting client fd & result length */
227   buf[offset] = '\0';
228   sscanf (buf, "%d/%d", &s, &result_len);
229
230   while (result_len > 0)
231     {
232       if ((len = read(0, buf, min2 (result_len, GSERV_BUFSZ - 1))) < 0)
233         {
234           perror (progname);
235           fprintf (stderr, "%s: unable to read\n", progname);
236           exit (1);
237         }
238
239       /* Send this string off, but only if we have enough space */
240
241       if (GSERV_BUFSZ > total)
242         {
243           if (total + len <= GSERV_BUFSZ)
244             buf[len] = 0;
245           else
246             buf[GSERV_BUFSZ - total] = 0;
247
248           send_string(s,buf);
249           total += strlen(buf);
250         }
251
252       result_len -= len;
253     }
254
255   /* eat the newline */
256   while ((len = read (0,buf,1)) == 0)
257     ;
258   if (len < 0)
259     {
260       perror(progname);
261       fprintf (stderr,"%s: unable to read\n", progname);
262       exit (1);
263     }
264   if (buf[0] != '\n')
265     {
266       fprintf (stderr,"%s: garbage after result [%c]\n", progname, buf[0]);
267       exit (1);
268     }
269 #endif
270
271   /* Send a response back to the client. */
272
273   msgp->mtype = msg_st.msg_lspid;
274   if (msgsnd (ipc_qid,msgp,strlen(msgp->mtext)+1,0) < 0)
275     perror ("msgsend(gnuserv)");
276
277 } /* handle_ipc_request */
278 #endif /* SYSV_IPC */
279
280
281 #if defined(INTERNET_DOMAIN_SOCKETS) || defined(UNIX_DOMAIN_SOCKETS)
282 /*
283   echo_request -- read request from a given socket descriptor, and send the information
284                   to stdout (the gnu process).
285 */
286 static void
287 echo_request (int s)
288 {
289   char buf[GSERV_BUFSZ];
290   int len;
291
292   printf("%d ",s);
293
294   /* read until we get a newline or no characters */
295   while ((len = recv(s,buf,GSERV_BUFSZ-1,0)) > 0) {
296     buf[len] = '\0';
297     printf("%s",buf);
298
299     if (buf[len-1] == EOT_CHR) {
300       fflush(stdout);
301       break;                    /* end of message */
302     }
303
304   } /* while */
305
306   if (len < 0) {
307     perror(progname);
308     fprintf(stderr,"%s: unable to recv\n",progname);
309     exit(1);
310   } /* if */
311
312 } /* echo_request */
313
314
315 /*
316   handle_response -- accept a response from stdin (the gnu process) and pass the
317                      information on to the relevant client.
318 */
319 static void
320 handle_response (void)
321 {
322   char buf[GSERV_BUFSZ+1];
323   int offset=0;
324   int s;
325   int len = 0;
326   int result_len;
327
328   /* read in "n/m:" (n=client fd, m=message length) */
329   while (offset < GSERV_BUFSZ &&
330          ((len = read(0,buf+offset,1)) > 0) &&
331          buf[offset] != ':') {
332     offset += len;
333   }
334
335   if (len < 0) {
336     perror(progname);
337     fprintf(stderr,"%s: unable to read\n",progname);
338     exit(1);
339   }
340
341   /* parse the response from emacs, getting client fd & result length */
342   buf[offset] = '\0';
343   sscanf(buf,"%d/%d", &s, &result_len);
344
345   while (result_len > 0) {
346     if ((len = read(0,buf,min2(result_len,GSERV_BUFSZ))) < 0) {
347       perror(progname);
348       fprintf(stderr,"%s: unable to read\n",progname);
349       exit(1);
350     }
351     buf[len] = '\0';
352     send_string(s,buf);
353     result_len -= len;
354   }
355
356   /* eat the newline */
357   while ((len = read(0,buf,1)) == 0)
358     ;
359   if (len < 0)
360     {
361       perror(progname);
362       fprintf(stderr,"%s: unable to read\n",progname);
363       exit(1);
364     }
365   if (buf[0] != '\n')
366     {
367       fprintf(stderr,"%s: garbage after result\n",progname);
368       exit(1);
369     }
370   /* send the newline */
371   buf[1] = '\0';
372   send_string(s,buf);
373   close(s);
374
375 } /* handle_response */
376 #endif /* INTERNET_DOMAIN_SOCKETS || UNIX_DOMAIN_SOCKETS */
377
378
379 #ifdef INTERNET_DOMAIN_SOCKETS
380 struct entry {
381   u_long host_addr;
382   struct entry *next;
383 };
384
385 struct entry *permitted_hosts[TABLE_SIZE];
386
387 #ifdef AUTH_MAGIC_COOKIE
388 # include <X11/X.h>
389 # include <X11/Xauth.h>
390
391 static Xauth *server_xauth = NULL;
392 #endif
393
394 static int
395 timed_read (int fd, char *buf, int max, int timeout, int one_line)
396 {
397   fd_set rmask;
398   struct timeval tv; /* = {timeout, 0}; */
399   char c = 0;
400   int nbytes = 0;
401   int r;
402
403   tv.tv_sec = timeout;
404   tv.tv_usec = 0;
405
406   FD_ZERO(&rmask);
407   FD_SET(fd, &rmask);
408
409   do
410     {
411       r = select(fd + 1, &rmask, NULL, NULL, &tv);
412
413       if (r > 0)
414         {
415           if (read (fd, &c, 1) == 1 )
416             {
417               *buf++ = c;
418               ++nbytes;
419             }
420           else
421             {
422               printf ("read error on socket\004\n");
423               return -1;
424             }
425         }
426       else if (r == 0)
427         {
428           printf ("read timed out\004\n");
429           return -1;
430         }
431       else
432         {
433           printf ("error in select\004\n");
434           return -1;
435         }
436     } while ((nbytes < max) &&  !(one_line && (c == '\n')));
437
438   --buf;
439   if (one_line && *buf == '\n')
440     {
441       *buf = 0;
442     }
443
444   return nbytes;
445 }
446
447
448
449 /*
450   permitted -- return whether a given host is allowed to connect to the server.
451 */
452 static int
453 permitted (u_long host_addr, int fd)
454 {
455   int key;
456   struct entry *entry;
457
458   char auth_protocol[128];
459   char buf[1024];
460   int  auth_data_len;
461   int  auth_data_pos;
462   int  auth_mismatches;
463
464   if (fd > 0)
465     {
466       /* we are checking permission on a real connection */
467
468       /* Read auth protocol name */
469
470       if (timed_read(fd, auth_protocol, AUTH_NAMESZ, AUTH_TIMEOUT, 1) <= 0)
471         return FALSE;
472
473       if (strcmp (auth_protocol, DEFAUTH_NAME) &&
474           strcmp (auth_protocol, MCOOKIE_NAME))
475         {
476           printf ("authentication protocol (%s) from client is invalid...\n",
477                   auth_protocol);
478           printf ("... Was the client an old version of gnuclient/gnudoit?\004\n");
479
480           return FALSE;
481         }
482
483       if (!strcmp(auth_protocol, MCOOKIE_NAME))
484         {
485
486           /*
487            * doing magic cookie auth
488            */
489
490           if (timed_read(fd, buf, 10, AUTH_TIMEOUT, 1) <= 0)
491             return FALSE;
492
493           auth_data_len = atoi(buf);
494
495           if (auth_data_len <= 0 || auth_data_len > sizeof(buf))
496               {
497                 return FALSE;
498               }
499
500           if (timed_read(fd, buf, auth_data_len, AUTH_TIMEOUT, 0) != auth_data_len)
501             return FALSE;
502
503 #ifdef AUTH_MAGIC_COOKIE
504           if (server_xauth && server_xauth->data)
505           {
506             /* Do a compare without comprising info about
507                the size of the cookie */
508             auth_mismatches =
509               ( auth_data_len ^
510                 server_xauth->data_length );
511
512             for(auth_data_pos=0; auth_data_pos < auth_data_len; ++auth_data_pos)
513               auth_mismatches |=
514                 ( buf[auth_data_pos] ^
515                   server_xauth->data[auth_data_pos % server_xauth->data_length]);
516
517             if (auth_mismatches == 0)
518               return TRUE;
519             
520             for(;rand() % 1000;);
521           }
522
523 #else
524           printf ("client tried Xauth, but server is not compiled with Xauth\n");
525 #endif
526
527       /*
528        * auth failed, but allow this to fall through to the GNU_SECURE
529        * protocol....
530        */
531
532           printf ("Xauth authentication failed, trying GNU_SECURE auth...\004\n");
533
534         }
535
536       /* Other auth protocols go here, and should execute only if the
537        * auth_protocol name matches.
538        */
539
540     }
541
542
543   /* Now, try the old GNU_SECURE stuff... */
544
545   /* First find the hash key */
546   key = HASH(host_addr) % TABLE_SIZE;
547
548   /* Now check the chain for that hash key */
549   for(entry=permitted_hosts[key]; entry != NULL; entry=entry->next)
550     if (host_addr == entry->host_addr)
551       return(TRUE);
552
553   return(FALSE);
554
555 } /* permitted */
556
557
558 /*
559   add_host -- add the given host to the list of permitted hosts, provided it isn't
560               already there.
561 */
562 static void
563 add_host (u_long host_addr)
564 {
565   int key;
566   struct entry *new_entry;
567
568   if (!permitted(host_addr, -1))
569     {
570       if ((new_entry = (struct entry *) malloc(sizeof(struct entry))) == NULL) {
571         fprintf(stderr,"%s: unable to malloc space for permitted host entry\n",
572                 progname);
573         exit(1);
574       } /* if */
575
576       new_entry->host_addr = host_addr;
577       key = HASH(host_addr) % TABLE_SIZE;
578       new_entry->next = permitted_hosts[key];
579       permitted_hosts[key] = new_entry;
580     } /* if */
581
582 } /* add_host */
583
584
585 /*
586   setup_table -- initialize the table of hosts allowed to contact the server,
587                  by reading from the file specified by the GNU_SECURE
588                  environment variable
589                  Put in the local machine, and, if a security file is specifed,
590                  add each host that is named in the file.
591                  Return the number of hosts added.
592 */
593 static int
594 setup_table (void)
595 {
596   FILE *host_file;
597   char *file_name;
598   char hostname[HOSTNAMSZ];
599   u_int host_addr;
600   int i, hosts=0;
601
602   /* Make sure every entry is null */
603   for (i=0; i<TABLE_SIZE; i++)
604     permitted_hosts[i] = NULL;
605
606   gethostname(hostname,HOSTNAMSZ);
607
608   if ((host_addr = internet_addr(hostname)) == -1)
609     {
610       fprintf(stderr,"%s: unable to find %s in /etc/hosts or from YP",
611               progname,hostname);
612       exit(1);
613     } /* if */
614
615 #ifdef AUTH_MAGIC_COOKIE
616
617   server_xauth = XauGetAuthByAddr (FamilyInternet,
618                                    sizeof(host_addr), (char *)&host_addr,
619                                    strlen(MCOOKIE_SCREEN), MCOOKIE_SCREEN,
620                                    strlen(MCOOKIE_X_NAME), MCOOKIE_X_NAME);
621   hosts++;
622
623 #endif /* AUTH_MAGIC_COOKIE */
624
625
626 #if 0 /* Don't even want to allow access from the local host by default */
627   add_host(host_addr);                                  /* add local host */
628 #endif
629
630   if (((file_name = getenv("GNU_SECURE")) != NULL &&    /* security file  */
631        (host_file = fopen(file_name,"r")) != NULL))     /* opened ok */
632     {
633       while ((fscanf(host_file,"%s",hostname) != EOF))  /* find a host */
634         if ((host_addr = internet_addr(hostname)) != -1)/* get its addr */
635           {
636             add_host(host_addr);                                /* add the addr */
637             hosts++;
638           }
639       fclose(host_file);
640     } /* if */
641
642   return hosts;
643 } /* setup_table */
644
645
646 /*
647   internet_init -- initialize server, returning an internet socket that can
648                     be listened on.
649 */
650 static int
651 internet_init (void)
652 {
653   int ls;                       /* socket descriptor */
654   struct servent *sp;           /* pointer to service information */
655   struct sockaddr_in server;    /* for local socket address */
656   char *ptr;                    /* ptr to return from getenv */
657
658   if (setup_table() == 0)
659     return -1;
660
661   /* clear out address structure */
662   memset (&server, '\0', sizeof (server));
663
664   /* Set up address structure for the listen socket. */
665   server.sin_family = AF_INET;
666   server.sin_addr.s_addr = INADDR_ANY;
667
668   /* Find the information for the gnu server
669    * in order to get the needed port number.
670    */
671   if ((ptr=getenv("GNU_PORT")) != NULL)
672     server.sin_port = htons(atoi(ptr));
673   else if ((sp = getservbyname ("gnuserv", "tcp")) == NULL)
674     server.sin_port = htons(DEFAULT_PORT+getuid());
675   else
676     server.sin_port = sp->s_port;
677
678   /* Create the listen socket. */
679   if ((ls = socket (AF_INET,SOCK_STREAM, 0)) == -1)
680     {
681       perror(progname);
682       fprintf(stderr,"%s: unable to create socket\n",progname);
683       exit(1);
684     } /* if */
685
686   /* Bind the listen address to the socket. */
687   if (bind(ls,(struct sockaddr *) &server,sizeof(struct sockaddr_in)) == -1)
688     {
689       perror(progname);
690       fprintf(stderr,"%s: unable to bind socket\n",progname);
691       exit(1);
692     } /* if */
693
694   /* Initiate the listen on the socket so remote users
695    * can connect.
696    */
697   if (listen(ls,20) == -1)
698     {
699       perror(progname);
700       fprintf(stderr,"%s: unable to listen\n",progname);
701       exit(1);
702     } /* if */
703
704   return(ls);
705
706 } /* internet_init */
707
708
709 /*
710   handle_internet_request -- accept a request from a client and send the information
711                              to stdout (the gnu process).
712 */
713 static void
714 handle_internet_request (int ls)
715 {
716   int s;
717   socklen_t addrlen = sizeof (struct sockaddr_in);
718   struct sockaddr_in peer;      /* for peer socket address */
719
720   memset (&peer, '\0', sizeof (peer));
721
722   if ((s = accept(ls,(struct sockaddr *)&peer, &addrlen)) == -1)
723     {
724       perror(progname);
725       fprintf(stderr,"%s: unable to accept\n",progname);
726       exit(1);
727     } /* if */
728
729   /* Check that access is allowed - if not return crud to the client */
730   if (!permitted(peer.sin_addr.s_addr, s))
731     {
732       send_string(s,"gnudoit: Connection refused\ngnudoit: unable to connect to remote");
733       close(s);
734
735       printf("Refused connection from %s\004\n", inet_ntoa(peer.sin_addr));
736       return;
737     } /* if */
738
739   echo_request(s);
740
741 } /* handle_internet_request */
742 #endif /* INTERNET_DOMAIN_SOCKETS */
743
744
745 #ifdef UNIX_DOMAIN_SOCKETS
746 /*
747   unix_init -- initialize server, returning an unix-domain socket that can
748                be listened on.
749 */
750 static int
751 unix_init (void)
752 {
753   int ls;                       /* socket descriptor */
754   struct sockaddr_un server;    /* unix socket address */
755   socklen_t bindlen;
756
757   if ((ls = socket(AF_UNIX,SOCK_STREAM, 0)) < 0)
758     {
759       perror(progname);
760       fprintf(stderr,"%s: unable to create socket\n",progname);
761       exit(1);
762     } /* if */
763
764   /* Set up address structure for the listen socket. */
765 #ifdef HIDE_UNIX_SOCKET
766   sprintf(server.sun_path,"%s/gsrvdir%d",tmpdir,(int)geteuid());
767   if (mkdir(server.sun_path, 0700) < 0)
768     {
769       /* assume it already exists, and try to set perms */
770       if (chmod(server.sun_path, 0700) < 0)
771         {
772           perror(progname);
773           fprintf(stderr,"%s: can't set permissions on %s\n",
774                   progname, server.sun_path);
775           exit(1);
776         }
777     }
778   strcat(server.sun_path,"/gsrv");
779   unlink(server.sun_path);      /* remove old file if it exists */
780 #else /* HIDE_UNIX_SOCKET */
781   sprintf(server.sun_path,"%s/gsrv%d",tmpdir,(int)geteuid());
782   unlink(server.sun_path);      /* remove old file if it exists */
783 #endif /* HIDE_UNIX_SOCKET */
784
785   server.sun_family = AF_UNIX;
786 #ifdef HAVE_SOCKADDR_SUN_LEN
787   /* See W. R. Stevens "Advanced Programming in the Unix Environment"
788      p. 502 */
789   bindlen = (sizeof (server.sun_len) + sizeof (server.sun_family)
790              + strlen (server.sun_path) + 1);
791   server.sun_len = bindlen;
792 #else
793   bindlen = strlen (server.sun_path) + sizeof (server.sun_family);
794 #endif
795
796   if (bind(ls,(struct sockaddr *)&server,bindlen) < 0)
797     {
798       perror(progname);
799       fprintf(stderr,"%s: unable to bind socket\n",progname);
800       exit(1);
801     } /* if */
802
803   chmod(server.sun_path,0700);  /* only this user can send commands */
804
805   if (listen(ls,20) < 0) {
806     perror(progname);
807     fprintf(stderr,"%s: unable to listen\n",progname);
808     exit(1);
809   } /* if */
810
811   /* #### there are also better ways of dealing with this when
812      sigvec() is present. */
813 #if  defined (HAVE_SIGPROCMASK)
814   {
815   sigset_t _mask;
816   sigemptyset (&_mask);
817   sigaddset (&_mask, SIGPIPE);
818   sigprocmask (SIG_BLOCK, &_mask, NULL);
819   }
820 #else
821   signal(SIGPIPE,SIG_IGN);      /* in case user kills client */
822 #endif
823
824   return(ls);
825
826 } /* unix_init */
827
828
829 /*
830   handle_unix_request -- accept a request from a client and send the information
831                          to stdout (the gnu process).
832 */
833 static void
834 handle_unix_request (int ls)
835 {
836   int s;
837   socklen_t len = sizeof (struct sockaddr_un);
838   struct sockaddr_un server;    /* for unix socket address */
839
840   server.sun_family = AF_UNIX;
841
842   if ((s = accept(ls,(struct sockaddr *)&server, &len)) < 0)
843     {
844       perror(progname);
845       fprintf(stderr,"%s: unable to accept\n",progname);
846     } /* if */
847
848   echo_request(s);
849
850 } /* handle_unix_request */
851 #endif /* UNIX_DOMAIN_SOCKETS */
852
853
854 int
855 main (int argc, char *argv[])
856 {
857   int chan;                     /* temporary channel number */
858 #ifdef SYSV_IPC
859   struct msgbuf *msgp;          /* message buffer */
860 #else
861   int ils = -1;                 /* internet domain listen socket */
862   int uls = -1;                 /* unix domain listen socket */
863 #endif /* SYSV_IPC */
864
865   progname = argv[0];
866
867   for(chan=3; chan < _NFILE; close(chan++)) /* close unwanted channels */
868     ;
869
870 #ifdef USE_TMPDIR
871   tmpdir = getenv("TMPDIR");
872 #endif
873   if (!tmpdir)
874     tmpdir = "/tmp";
875 #ifdef USE_LITOUT
876   {
877     /* this is to allow ^D to pass to emacs */
878     int d = LLITOUT;
879     (void) ioctl(fileno(stdout), TIOCLBIS, &d);
880   }
881 #endif
882
883 #ifdef SYSV_IPC
884   ipc_init(&msgp);              /* get a msqid to listen on, and a message buffer */
885 #endif /* SYSV_IPC */
886
887 #ifdef INTERNET_DOMAIN_SOCKETS
888   ils = internet_init();        /* get an internet domain socket to listen on */
889 #endif /* INTERNET_DOMAIN_SOCKETS */
890
891 #ifdef UNIX_DOMAIN_SOCKETS
892   uls = unix_init();            /* get a unix domain socket to listen on */
893 #endif /* UNIX_DOMAIN_SOCKETS */
894
895   while (1) {
896 #ifdef SYSV_IPC
897     handle_ipc_request(msgp);
898 #else /* NOT SYSV_IPC */
899     fd_set rmask;
900     FD_ZERO(&rmask);
901     FD_SET(fileno(stdin), &rmask);
902     if (uls >= 0)
903       FD_SET(uls, &rmask);
904     if (ils >= 0)
905       FD_SET(ils, &rmask);
906
907     if (select(max2(fileno(stdin),max2(uls,ils)) + 1, &rmask,
908                (fd_set *)NULL, (fd_set *)NULL, (struct timeval *)NULL) < 0)
909       {
910         perror(progname);
911         fprintf(stderr,"%s: unable to select\n",progname);
912         return 1;
913       } /* if */
914
915 #ifdef UNIX_DOMAIN_SOCKETS
916     if (uls > 0 && FD_ISSET(uls, &rmask))
917       handle_unix_request(uls);
918 #endif
919
920 #ifdef INTERNET_DOMAIN_SOCKETS
921     if (ils > 0 && FD_ISSET(ils, &rmask))
922       handle_internet_request(ils);
923 #endif /* INTERNET_DOMAIN_SOCKETS */
924
925     if (FD_ISSET(fileno(stdin), &rmask))      /* from stdin (gnu process) */
926       handle_response();
927 #endif /* NOT SYSV_IPC */
928   } /* while (1) */
929 } /* main */
930
931 #endif /* SYSV_IPC || UNIX_DOMAIN_SOCKETS || INTERNET_DOMAIN_SOCKETS */