Revert "Fix the fix, make pi a normal lisp var (Closes bug #176)"
[sxemacs] / lib-src / gnuslib.c
1 /* -*-C-*-
2  Common library code for the GNU Emacs server and client.
3
4  This file is part of GNU Emacs.
5
6  Copying is permitted under those conditions described by the GNU
7  General Public License.
8
9  Copyright (C) 1989 Free Software Foundation, Inc.
10
11  Author: Andy Norman (ange@hplb.hpl.hp.com), based on
12          'etc/server.c' and 'etc/emacsclient.c' from the 18.52 GNU
13          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 <errno.h>
34 #include <assert.h>
35
36 #ifdef SYSV_IPC
37 static int connect_to_ipc_server(void);
38 #endif
39 #ifdef UNIX_DOMAIN_SOCKETS
40 static int connect_to_unix_server(void);
41 #endif
42 #ifdef INTERNET_DOMAIN_SOCKETS
43 static int connect_to_internet_server(char *serverhost, unsigned short port);
44 #endif
45
46 /* On some systems, e.g. DGUX, inet_addr returns a 'struct in_addr'. */
47 #ifdef HAVE_BROKEN_INET_ADDR
48 # define IN_ADDR struct in_addr
49 # define NUMERIC_ADDR_ERROR (numeric_addr.s_addr == -1)
50 #else
51 # if SIZEOF_LONG > 4
52 #  define IN_ADDR unsigned int
53 # else
54 #  define IN_ADDR unsigned long
55 # endif
56 # define NUMERIC_ADDR_ERROR (numeric_addr == (IN_ADDR) -1)
57 #endif
58
59 #include <stdlib.h>
60 #include <stdio.h>
61 #include <sys/types.h>
62 #include <sys/stat.h>
63 #ifdef HAVE_UNISTD_H
64 #include <unistd.h>
65 #endif                          /* HAVE_UNISTD_H */
66 #ifdef HAVE_STRING_H
67 #include <string.h>
68 #endif                          /* HAVE_STRING_H */
69
70 #include <arpa/inet.h>
71
72 char *tmpdir = NULL;
73
74 char *progname = NULL;
75
76 int make_connection(char *hostarg, int portarg, int *s)
77 {
78 #ifdef INTERNET_DOMAIN_SOCKETS
79         char *ptr;
80         if (hostarg == NULL)
81                 hostarg = getenv("GNU_HOST");
82         if (portarg == 0 && (ptr = getenv("GNU_PORT")) != NULL)
83                 portarg = atoi(ptr);
84 #endif
85
86         if (hostarg != NULL) {
87                 /* hostname was given explicitly, via cmd line arg or GNU_HOST,
88                  * so obey it. */
89 #ifdef UNIX_DOMAIN_SOCKETS
90                 if (!strcmp(hostarg, "unix")) {
91                         *s = connect_to_unix_server();
92                         return (int)CONN_UNIX;
93                 }
94 #endif                          /* UNIX_DOMAIN_SOCKETS */
95 #ifdef INTERNET_DOMAIN_SOCKETS
96                 *s = connect_to_internet_server(hostarg, portarg);
97                 return (int)CONN_INTERNET;
98 #endif
99 #ifdef SYSV_IPC
100                 return -1;      /* hostarg should always be NULL for SYSV_IPC */
101 #endif
102         } else {
103                 /* no hostname given.  Use unix-domain/sysv-ipc, or
104                  * internet-domain connection to local host if they're not available. */
105 #if   defined(UNIX_DOMAIN_SOCKETS)
106                 *s = connect_to_unix_server();
107                 return (int)CONN_UNIX;
108 #elif defined(SYSV_IPC)
109                 *s = connect_to_ipc_server();
110                 return (int)CONN_IPC;
111 #elif defined(INTERNET_DOMAIN_SOCKETS)
112                 {
113                         char localhost[HOSTNAMSZ];
114                         gethostname(localhost, HOSTNAMSZ);      /* use this host by default */
115                         *s = connect_to_internet_server(localhost, portarg);
116                         return (int)CONN_INTERNET;
117                 }
118 #endif                          /* IPC type */
119         }
120 }
121
122 #ifdef SYSV_IPC
123 /*
124   connect_to_ipc_server -- establish connection with server process via SYSV IPC
125                            Returns msqid for server if successful.
126 */
127 static int connect_to_ipc_server(void)
128 {
129         int s;                  /* connected msqid */
130         key_t key;              /* message key */
131         char buf[GSERV_BUFSZ + 1];      /* buffer for filename */
132
133         int sz;
134
135         SNPRINTF(sz, buf, sizeof(buf), "%s/gsrv%d", tmpdir, (int)geteuid());
136         creat(buf, 0600);
137         if ((key = ftok(buf, 1)) == -1) {
138                 perror(progname);
139                 fprintf(stderr, "%s: unable to get ipc key from %s\n",
140                         progname, buf);
141                 exit(1);
142         }
143
144         if ((s = msgget(key, 0600)) == -1) {
145                 perror(progname);
146                 fprintf(stderr, "%s: unable to access msg queue\n", progname);
147                 exit(1);
148         };                      /* if */
149
150         return (s);
151
152 }                               /* connect_to_ipc_server */
153
154 /*
155   disconnect_from_ipc_server -- inform the server that sending has finished,
156                                 and wait for its reply.
157 */
158 void disconnect_from_ipc_server(int s, struct msgbuf *msgp, int echo)
159 {
160         int len;                /* length of received message */
161
162         send_string(s, EOT_STR);        /* EOT terminates this message */
163         msgp->mtype = 1;
164
165         if (msgsnd(s, msgp, strlen(msgp->mtext) + 1, 0) < 0) {
166                 perror(progname);
167                 fprintf(stderr, "%s: unable to send message to server\n",
168                         progname);
169                 exit(1);
170         };                      /* if */
171
172         if ((len = msgrcv(s, msgp, GSERV_BUFSZ, getpid(), 0)) < 0) {
173                 perror(progname);
174                 fprintf(stderr, "%s: unable to receive message from server\n",
175                         progname);
176                 exit(1);
177         };                      /* if */
178
179         if (echo) {
180                 msgp->mtext[len] = '\0';        /* string terminate message */
181                 fputs(msgp->mtext, stdout);
182                 if (msgp->mtext[len - 1] != '\n')
183                         putchar('\n');
184         };                      /* if */
185
186 }                               /* disconnect_from_ipc_server */
187 #endif                          /* SYSV_IPC */
188
189 #if defined(INTERNET_DOMAIN_SOCKETS) || defined(UNIX_DOMAIN_SOCKETS)
190 /*
191   send_string -- send string to socket.
192 */
193 void send_string(int s, const char *msg)
194 {
195 #if 0
196         if (send(s, msg, strlen(msg), 0) < 0) {
197                 perror(progname);
198                 fprintf(stderr, "%s: unable to send\n", progname);
199                 exit(1);
200         };                      /* if */
201 #else
202         int len, left = strlen(msg);
203         while (left > 0) {
204                 if ((len = write(s, msg, min2(left, GSERV_BUFSZ))) < 0) {
205                         /* XEmacs addition: robertl@arnet.com */
206                         if (errno == EPIPE) {
207                                 return;
208                         }
209                         perror(progname);
210                         fprintf(stderr, "%s: unable to send\n", progname);
211                         exit(1);
212                 };              /* if */
213                 left -= len;
214                 msg += len;
215         };                      /* while */
216 #endif
217 }                               /* send_string */
218
219 /*
220   read_line -- read a \n terminated line from a socket
221 */
222 int read_line(int s, char *dest)
223 {
224         int length;
225         int offset = 0;
226         char buffer[GSERV_BUFSZ + 1];
227
228         while ((length = read(s, buffer + offset, 1) > 0)
229                && buffer[offset] != '\n' && buffer[offset] != EOT_CHR) {
230                 offset += length;
231                 if (offset >= GSERV_BUFSZ)
232                         break;
233         }
234         buffer[offset] = '\0';
235         strcpy(dest, buffer);
236         return 1;
237 }                               /* read_line */
238 #endif                          /* INTERNET_DOMAIN_SOCKETS || UNIX_DOMAIN_SOCKETS */
239
240 #ifdef UNIX_DOMAIN_SOCKETS
241 /*
242   connect_to_unix_server -- establish connection with server process via a unix-
243                             domain socket. Returns socket descriptor for server
244                             if successful.
245 */
246 static int connect_to_unix_server(void)
247 {
248         int s;                  /* connected socket descriptor */
249         struct sockaddr_un server;      /* for unix connections */
250         int sz;
251
252         if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
253                 perror(progname);
254                 fprintf(stderr, "%s: unable to create socket\n", progname);
255                 exit(1);
256         };                      /* if */
257
258         server.sun_family = AF_UNIX;
259 #ifdef HIDE_UNIX_SOCKET
260         SNPRINTF(sz, server.sun_path, sizeof(server.sun_path),
261                  "%s/gsrvdir%d/gsrv", tmpdir, (int)geteuid());
262 #else                           /* HIDE_UNIX_SOCKET */
263         SNPRINTF(sz, server.sun_path, sizeof(server.sun_path),
264                  "%s/gsrv%d", tmpdir, (int)geteuid());
265 #endif                          /* HIDE_UNIX_SOCKET */
266         if (connect(s, (struct sockaddr *)&server, strlen(server.sun_path) + 2)
267             < 0) {
268                 perror(progname);
269                 fprintf(stderr, "%s: unable to connect to local\n", progname);
270                 exit(1);
271         };                      /* if */
272
273         return (s);
274
275 }                               /* connect_to_unix_server */
276 #endif                          /* UNIX_DOMAIN_SOCKETS */
277
278 #ifdef INTERNET_DOMAIN_SOCKETS
279 /*
280   internet_addr -- return the internet addr of the hostname or
281                    internet address passed. Return -1 on error.
282 */
283 int internet_addr(char *host)
284 {
285         struct hostent *hp;     /* pointer to host info for remote host */
286         IN_ADDR numeric_addr;   /* host address */
287
288         numeric_addr = inet_addr(host);
289         if (!NUMERIC_ADDR_ERROR)
290                 return numeric_addr;
291         else if ((hp = gethostbyname(host)) != NULL)
292                 return ((struct in_addr *)(hp->h_addr))->s_addr;
293         else
294                 return -1;
295
296 }                               /* internet_addr */
297
298 #ifdef AUTH_MAGIC_COOKIE
299 # include <X11/X.h>
300 # include <X11/Xauth.h>
301
302 static Xauth *server_xauth = NULL;
303 #endif
304
305 /*
306   connect_to_internet_server -- establish connection with server process via
307                                 an internet domain socket. Returns socket
308                                 descriptor for server if successful.
309 */
310 static int connect_to_internet_server(char *serverhost, unsigned short port)
311 {
312         int s;                  /* connected socket descriptor */
313         struct servent *sp;     /* pointer to service information */
314         struct sockaddr_in peeraddr_in; /* for peer socket address */
315         char buf[512];          /* temporary buffer */
316
317         int t, len;
318
319         /* clear out address structures */
320         memset((char *)&peeraddr_in, 0, sizeof(struct sockaddr_in));
321
322         /* Set up the peer address to which we will connect. */
323         peeraddr_in.sin_family = AF_INET;
324
325         /* look up the server host's internet address */
326         if ((t = internet_addr(serverhost)) == -1) {
327                 fprintf(stderr,
328                         "%s: unable to find %s in /etc/hosts or from YP\n",
329                         progname, serverhost);
330                 exit(1);
331         } else {
332                 peeraddr_in.sin_addr.s_addr = t;
333         };                      /* if */
334
335         if (port == 0) {
336                 if ((sp = getservbyname("gnuserv", "tcp")) == NULL)
337                         peeraddr_in.sin_port = htons(DEFAULT_PORT + getuid());
338                 else
339                         peeraddr_in.sin_port = sp->s_port;
340         } /* if */
341         else
342                 peeraddr_in.sin_port = htons(port);
343
344         /* Create the socket. */
345         if ((s = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
346                 perror(progname);
347                 fprintf(stderr, "%s: unable to create socket\n", progname);
348                 exit(1);
349         };                      /* if */
350
351         /* Try to connect to the remote server at the address
352          * which was just built into peeraddr.
353          */
354         if (connect(s, (struct sockaddr *)&peeraddr_in,
355                     sizeof(struct sockaddr_in)) == -1) {
356                 perror(progname);
357                 fprintf(stderr, "%s: unable to connect to remote\n", progname);
358                 exit(1);
359         };                      /* if */
360
361 #ifdef AUTH_MAGIC_COOKIE
362
363         /* send credentials using MIT-MAGIC-COOKIE-1 protocol */
364
365         server_xauth =
366             XauGetAuthByAddr(FamilyInternet,
367                              sizeof(peeraddr_in.sin_addr.s_addr),
368                              (char *)&peeraddr_in.sin_addr.s_addr,
369                              strlen(MCOOKIE_SCREEN), MCOOKIE_SCREEN,
370                              strlen(MCOOKIE_X_NAME), MCOOKIE_X_NAME);
371
372         if (server_xauth && server_xauth->data) {
373                 SNPRINTF(len, buf, sizeof(buf),
374                          "%s\n%d\n", MCOOKIE_NAME, server_xauth->data_length);
375                 t = write(s, buf, len);
376                 if(t != len) {
377                         fprintf(stderr, "%s: unable to send auth", progname);
378                         exit(1);
379                 }
380                 t = write(s, server_xauth->data, server_xauth->data_length);
381                 if(t !=  server_xauth->data_length) {
382                         fprintf(stderr, "%s: unable to send auth", progname);
383                         exit(1);
384                 }
385                 return (s);
386         }
387 #endif                          /* AUTH_MAGIC_COOKIE */
388
389         SNPRINTF(len, buf, sizeof(buf), "%s\n", DEFAUTH_NAME);
390         t = write(s, buf, len);
391         if(t != len) {
392                 fprintf(stderr, "%s: unable to send auth", progname);
393                 exit(1);
394         }
395
396         return (s);
397
398 }                               /* connect_to_internet_server */
399 #endif                          /* INTERNET_DOMAIN_SOCKETS */
400
401 #if defined(INTERNET_DOMAIN_SOCKETS) || defined(UNIX_DOMAIN_SOCKETS)
402 /*
403   disconnect_from_server -- inform the server that sending has finished, and wait for
404                             its reply.
405 */
406 void disconnect_from_server(int s, int echo)
407 {
408 #if 0
409         char buffer[REPLYSIZ + 1];
410 #else
411         char buffer[GSERV_BUFSZ + 1];
412 #endif
413         int add_newline = 1;
414         int length;
415
416         send_string(s, EOT_STR);        /* make sure server gets string */
417
418 #if !defined (linux)  && !defined (_SCO_DS)
419         /*
420          * shutdown is completely hozed under linux. If s is a unix domain socket,
421          * you'll get EOPNOTSUPP back from it. If s is an internet socket, you get
422          * a broken pipe when you try to read a bit later. The latter
423          * problem is fixed for linux versions >= 1.1.46, but the problem
424          * with unix sockets persists. Sigh.
425          */
426
427         if (shutdown(s, 1) == -1) {
428                 perror(progname);
429                 fprintf(stderr, "%s: unable to shutdown socket\n", progname);
430                 exit(1);
431         };                      /* if */
432 #endif
433
434 #if 0
435         while ((length = recv(s, buffer, REPLYSIZ, 0)) > 0) {
436                 buffer[length] = '\0';
437                 if (echo)
438                         fputs(buffer, stdout);
439                 add_newline = (buffer[length - 1] != '\n');
440         };                      /* while */
441 #else
442         while ((length = read(s, buffer, GSERV_BUFSZ)) > 0 ||
443                (length == -1 && errno == EINTR)) {
444                 if (length>0) {
445                         buffer[length] = '\0';
446                         if (echo) {
447                                 fputs(buffer, stdout);
448                                 add_newline = (buffer[length - 1] != '\n');
449                         };      /* if */
450                 };              /* if */
451         };                      /* while */
452 #endif
453
454         if (echo && add_newline)
455                 putchar('\n');
456
457         if (length < 0) {
458                 perror(progname);
459                 fprintf(stderr,
460                         "%s: unable to read the reply from the server\n",
461                         progname);
462                 exit(1);
463         };                      /* if */
464
465 }                               /* disconnect_from_server */
466 #endif                          /* INTERNET_DOMAIN_SOCKETS || UNIX_DOMAIN_SOCKETS */