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