XEmacs 21.4.2 "Developer-Friendly Unix APIs".
[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   unsigned 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 (unsigned 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
462   if (fd > 0)
463     {
464       /* we are checking permission on a real connection */
465
466       /* Read auth protocol name */
467
468       if (timed_read(fd, auth_protocol, AUTH_NAMESZ, AUTH_TIMEOUT, 1) <= 0)
469         return FALSE;
470
471       if (strcmp (auth_protocol, DEFAUTH_NAME) &&
472           strcmp (auth_protocol, MCOOKIE_NAME))
473         {
474           printf ("authentication protocol (%s) from client is invalid...\n",
475                   auth_protocol);
476           printf ("... Was the client an old version of gnuclient/gnudoit?\004\n");
477
478           return FALSE;
479         }
480
481       if (!strcmp(auth_protocol, MCOOKIE_NAME))
482         {
483
484           /*
485            * doing magic cookie auth
486            */
487
488           if (timed_read(fd, buf, 10, AUTH_TIMEOUT, 1) <= 0)
489             return FALSE;
490
491           auth_data_len = atoi(buf);
492
493           if (auth_data_len <= 0 || auth_data_len > sizeof(buf))
494               {
495                 return FALSE;
496               }
497
498           if (timed_read(fd, buf, auth_data_len, AUTH_TIMEOUT, 0) != auth_data_len)
499             return FALSE;
500
501 #ifdef AUTH_MAGIC_COOKIE
502           if (server_xauth && server_xauth->data)
503             {
504             /* Do a compare without comprising info about
505                the size of the cookie */
506             int auth_data_pos;
507             int auth_mismatches =
508               ( auth_data_len ^
509                 server_xauth->data_length );
510
511             for(auth_data_pos=0; auth_data_pos < auth_data_len; ++auth_data_pos)
512               auth_mismatches |=
513                 ( buf[auth_data_pos] ^
514                   server_xauth->data[auth_data_pos % server_xauth->data_length]);
515
516             if (auth_mismatches == 0)
517               return TRUE;
518             
519             for(;rand() % 1000;);
520             }
521
522 #else
523           printf ("client tried Xauth, but server is not compiled with Xauth\n");
524 #endif
525
526       /*
527        * auth failed, but allow this to fall through to the GNU_SECURE
528        * protocol....
529        */
530
531           printf ("Xauth authentication failed, trying GNU_SECURE auth...\004\n");
532
533         }
534
535       /* Other auth protocols go here, and should execute only if the
536        * auth_protocol name matches.
537        */
538
539     }
540
541
542   /* Now, try the old GNU_SECURE stuff... */
543
544   /* First find the hash key */
545   key = HASH(host_addr) % TABLE_SIZE;
546
547   /* Now check the chain for that hash key */
548   for(entry=permitted_hosts[key]; entry != NULL; entry=entry->next)
549     if (host_addr == entry->host_addr)
550       return(TRUE);
551
552   return(FALSE);
553
554 } /* permitted */
555
556
557 /*
558   add_host -- add the given host to the list of permitted hosts, provided it isn't
559               already there.
560 */
561 static void
562 add_host (unsigned long host_addr)
563 {
564   int key;
565   struct entry *new_entry;
566
567   if (!permitted(host_addr, -1))
568     {
569       if ((new_entry = (struct entry *) malloc(sizeof(struct entry))) == NULL) {
570         fprintf(stderr,"%s: unable to malloc space for permitted host entry\n",
571                 progname);
572         exit(1);
573       } /* if */
574
575       new_entry->host_addr = host_addr;
576       key = HASH(host_addr) % TABLE_SIZE;
577       new_entry->next = permitted_hosts[key];
578       permitted_hosts[key] = new_entry;
579     } /* if */
580
581 } /* add_host */
582
583
584 /*
585   setup_table -- initialize the table of hosts allowed to contact the server,
586                  by reading from the file specified by the GNU_SECURE
587                  environment variable
588                  Put in the local machine, and, if a security file is specifed,
589                  add each host that is named in the file.
590                  Return the number of hosts added.
591 */
592 static int
593 setup_table (void)
594 {
595   FILE *host_file;
596   char *file_name;
597   char hostname[HOSTNAMSZ];
598   unsigned int host_addr;
599   int i, hosts=0;
600
601   /* Make sure every entry is null */
602   for (i=0; i<TABLE_SIZE; i++)
603     permitted_hosts[i] = NULL;
604
605   gethostname(hostname,HOSTNAMSZ);
606
607   if ((host_addr = internet_addr(hostname)) == -1)
608     {
609       fprintf(stderr,"%s: unable to find %s in /etc/hosts or from YP",
610               progname,hostname);
611       exit(1);
612     } /* if */
613
614 #ifdef AUTH_MAGIC_COOKIE
615
616   server_xauth = XauGetAuthByAddr (FamilyInternet,
617                                    sizeof(host_addr), (char *)&host_addr,
618                                    strlen(MCOOKIE_SCREEN), MCOOKIE_SCREEN,
619                                    strlen(MCOOKIE_X_NAME), MCOOKIE_X_NAME);
620   hosts++;
621
622 #endif /* AUTH_MAGIC_COOKIE */
623
624
625 #if 0 /* Don't even want to allow access from the local host by default */
626   add_host(host_addr);                                  /* add local host */
627 #endif
628
629   if (((file_name = getenv("GNU_SECURE")) != NULL &&    /* security file  */
630        (host_file = fopen(file_name,"r")) != NULL))     /* opened ok */
631     {
632       while ((fscanf(host_file,"%s",hostname) != EOF))  /* find a host */
633         if ((host_addr = internet_addr(hostname)) != -1)/* get its addr */
634           {
635             add_host(host_addr);                                /* add the addr */
636             hosts++;
637           }
638       fclose(host_file);
639     } /* if */
640
641   return hosts;
642 } /* setup_table */
643
644
645 /*
646   internet_init -- initialize server, returning an internet socket that can
647                     be listened on.
648 */
649 static int
650 internet_init (void)
651 {
652   int ls;                       /* socket descriptor */
653   struct servent *sp;           /* pointer to service information */
654   struct sockaddr_in server;    /* for local socket address */
655   char *ptr;                    /* ptr to return from getenv */
656
657   if (setup_table() == 0)
658     return -1;
659
660   /* clear out address structure */
661   memset (&server, '\0', sizeof (server));
662
663   /* Set up address structure for the listen socket. */
664   server.sin_family = AF_INET;
665   server.sin_addr.s_addr = INADDR_ANY;
666
667   /* Find the information for the gnu server
668    * in order to get the needed port number.
669    */
670   if ((ptr=getenv("GNU_PORT")) != NULL)
671     server.sin_port = htons(atoi(ptr));
672   else if ((sp = getservbyname ("gnuserv", "tcp")) == NULL)
673     server.sin_port = htons(DEFAULT_PORT+getuid());
674   else
675     server.sin_port = sp->s_port;
676
677   /* Create the listen socket. */
678   if ((ls = socket (AF_INET,SOCK_STREAM, 0)) == -1)
679     {
680       perror(progname);
681       fprintf(stderr,"%s: unable to create socket\n",progname);
682       exit(1);
683     } /* if */
684
685   /* Bind the listen address to the socket. */
686   if (bind(ls,(struct sockaddr *) &server,sizeof(struct sockaddr_in)) == -1)
687     {
688       perror(progname);
689       fprintf(stderr,"%s: unable to bind socket\n",progname);
690       exit(1);
691     } /* if */
692
693   /* Initiate the listen on the socket so remote users
694    * can connect.
695    */
696   if (listen(ls,20) == -1)
697     {
698       perror(progname);
699       fprintf(stderr,"%s: unable to listen\n",progname);
700       exit(1);
701     } /* if */
702
703   return(ls);
704
705 } /* internet_init */
706
707
708 /*
709   handle_internet_request -- accept a request from a client and send the information
710                              to stdout (the gnu process).
711 */
712 static void
713 handle_internet_request (int ls)
714 {
715   int s;
716   socklen_t addrlen = sizeof (struct sockaddr_in);
717   struct sockaddr_in peer;      /* for peer socket address */
718
719   memset (&peer, '\0', sizeof (peer));
720
721   if ((s = accept(ls,(struct sockaddr *)&peer, &addrlen)) == -1)
722     {
723       perror(progname);
724       fprintf(stderr,"%s: unable to accept\n",progname);
725       exit(1);
726     } /* if */
727
728   /* Check that access is allowed - if not return crud to the client */
729   if (!permitted(peer.sin_addr.s_addr, s))
730     {
731       send_string(s,"gnudoit: Connection refused\ngnudoit: unable to connect to remote");
732       close(s);
733
734       printf("Refused connection from %s\004\n", inet_ntoa(peer.sin_addr));
735       return;
736     } /* if */
737
738   echo_request(s);
739
740 } /* handle_internet_request */
741 #endif /* INTERNET_DOMAIN_SOCKETS */
742
743
744 #ifdef UNIX_DOMAIN_SOCKETS
745 /*
746   unix_init -- initialize server, returning an unix-domain socket that can
747                be listened on.
748 */
749 static int
750 unix_init (void)
751 {
752   int ls;                       /* socket descriptor */
753   struct sockaddr_un server;    /* unix socket address */
754   socklen_t bindlen;
755
756   if ((ls = socket(AF_UNIX,SOCK_STREAM, 0)) < 0)
757     {
758       perror(progname);
759       fprintf(stderr,"%s: unable to create socket\n",progname);
760       exit(1);
761     } /* if */
762
763   /* Set up address structure for the listen socket. */
764 #ifdef HIDE_UNIX_SOCKET
765   sprintf(server.sun_path,"%s/gsrvdir%d",tmpdir,(int)geteuid());
766   if (mkdir(server.sun_path, 0700) < 0)
767     {
768       /* assume it already exists, and try to set perms */
769       if (chmod(server.sun_path, 0700) < 0)
770         {
771           perror(progname);
772           fprintf(stderr,"%s: can't set permissions on %s\n",
773                   progname, server.sun_path);
774           exit(1);
775         }
776     }
777   strcat(server.sun_path,"/gsrv");
778   unlink(server.sun_path);      /* remove old file if it exists */
779 #else /* HIDE_UNIX_SOCKET */
780   sprintf(server.sun_path,"%s/gsrv%d",tmpdir,(int)geteuid());
781   unlink(server.sun_path);      /* remove old file if it exists */
782 #endif /* HIDE_UNIX_SOCKET */
783
784   server.sun_family = AF_UNIX;
785 #ifdef HAVE_SOCKADDR_SUN_LEN
786   /* See W. R. Stevens "Advanced Programming in the Unix Environment"
787      p. 502 */
788   bindlen = (sizeof (server.sun_len) + sizeof (server.sun_family)
789              + strlen (server.sun_path) + 1);
790   server.sun_len = bindlen;
791 #else
792   bindlen = strlen (server.sun_path) + sizeof (server.sun_family);
793 #endif
794
795   if (bind(ls,(struct sockaddr *)&server,bindlen) < 0)
796     {
797       perror(progname);
798       fprintf(stderr,"%s: unable to bind socket\n",progname);
799       exit(1);
800     } /* if */
801
802   chmod(server.sun_path,0700);  /* only this user can send commands */
803
804   if (listen(ls,20) < 0) {
805     perror(progname);
806     fprintf(stderr,"%s: unable to listen\n",progname);
807     exit(1);
808   } /* if */
809
810   /* #### there are also better ways of dealing with this when
811      sigvec() is present. */
812 #if  defined (HAVE_SIGPROCMASK)
813   {
814   sigset_t _mask;
815   sigemptyset (&_mask);
816   sigaddset (&_mask, SIGPIPE);
817   sigprocmask (SIG_BLOCK, &_mask, NULL);
818   }
819 #else
820   signal(SIGPIPE,SIG_IGN);      /* in case user kills client */
821 #endif
822
823   return(ls);
824
825 } /* unix_init */
826
827
828 /*
829   handle_unix_request -- accept a request from a client and send the information
830                          to stdout (the gnu process).
831 */
832 static void
833 handle_unix_request (int ls)
834 {
835   int s;
836   socklen_t len = sizeof (struct sockaddr_un);
837   struct sockaddr_un server;    /* for unix socket address */
838
839   server.sun_family = AF_UNIX;
840
841   if ((s = accept(ls,(struct sockaddr *)&server, &len)) < 0)
842     {
843       perror(progname);
844       fprintf(stderr,"%s: unable to accept\n",progname);
845     } /* if */
846
847   echo_request(s);
848
849 } /* handle_unix_request */
850 #endif /* UNIX_DOMAIN_SOCKETS */
851
852
853 int
854 main (int argc, char *argv[])
855 {
856   int chan;                     /* temporary channel number */
857 #ifdef SYSV_IPC
858   struct msgbuf *msgp;          /* message buffer */
859 #else
860   int ils = -1;                 /* internet domain listen socket */
861   int uls = -1;                 /* unix domain listen socket */
862 #endif /* SYSV_IPC */
863
864   progname = argv[0];
865
866   for(chan=3; chan < _NFILE; close(chan++)) /* close unwanted channels */
867     ;
868
869 #ifdef USE_TMPDIR
870   tmpdir = getenv("TMPDIR");
871 #endif
872   if (!tmpdir)
873     tmpdir = "/tmp";
874 #ifdef USE_LITOUT
875   {
876     /* this is to allow ^D to pass to emacs */
877     int d = LLITOUT;
878     (void) ioctl(fileno(stdout), TIOCLBIS, &d);
879   }
880 #endif
881
882 #ifdef SYSV_IPC
883   ipc_init(&msgp);              /* get a msqid to listen on, and a message buffer */
884 #endif /* SYSV_IPC */
885
886 #ifdef INTERNET_DOMAIN_SOCKETS
887   ils = internet_init();        /* get an internet domain socket to listen on */
888 #endif /* INTERNET_DOMAIN_SOCKETS */
889
890 #ifdef UNIX_DOMAIN_SOCKETS
891   uls = unix_init();            /* get a unix domain socket to listen on */
892 #endif /* UNIX_DOMAIN_SOCKETS */
893
894   while (1) {
895 #ifdef SYSV_IPC
896     handle_ipc_request(msgp);
897 #else /* NOT SYSV_IPC */
898     fd_set rmask;
899     FD_ZERO(&rmask);
900     FD_SET(fileno(stdin), &rmask);
901     if (uls >= 0)
902       FD_SET(uls, &rmask);
903     if (ils >= 0)
904       FD_SET(ils, &rmask);
905
906     if (select(max2(fileno(stdin),max2(uls,ils)) + 1, &rmask,
907                (fd_set *)NULL, (fd_set *)NULL, (struct timeval *)NULL) < 0)
908       {
909         perror(progname);
910         fprintf(stderr,"%s: unable to select\n",progname);
911         return 1;
912       } /* if */
913
914 #ifdef UNIX_DOMAIN_SOCKETS
915     if (uls > 0 && FD_ISSET(uls, &rmask))
916       handle_unix_request(uls);
917 #endif
918
919 #ifdef INTERNET_DOMAIN_SOCKETS
920     if (ils > 0 && FD_ISSET(ils, &rmask))
921       handle_internet_request(ils);
922 #endif /* INTERNET_DOMAIN_SOCKETS */
923
924     if (FD_ISSET(fileno(stdin), &rmask))      /* from stdin (gnu process) */
925       handle_response();
926 #endif /* NOT SYSV_IPC */
927   } /* while (1) */
928 } /* main */
929
930 #endif /* SYSV_IPC || UNIX_DOMAIN_SOCKETS || INTERNET_DOMAIN_SOCKETS */