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