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