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