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