Merge remote-tracking branch 'njsf/for-steve' into njsf-dbus
[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 #include <assert.h>
34
35 char gnuserv_version[] = "gnuserv version" GNUSERV_VERSION;
36
37 #ifdef USE_LITOUT
38 #ifdef linux
39 #include <bsd/sgtty.h>
40 #else
41 #include <sgtty.h>
42 #endif
43 #endif
44
45 #ifdef AIX
46 #include <sys/select.h>
47 #endif
48
49 #include <stdlib.h>
50 #include <stdio.h>
51 #include <sys/types.h>
52 #include <sys/stat.h>
53
54 #ifdef HAVE_UNISTD_H
55 #include <unistd.h>
56 #endif                          /* HAVE_UNISTD_H */
57
58 #ifdef HAVE_STRING_H
59 #include <string.h>
60 #endif                          /* HAVE_STRING_H */
61
62
63 #if !defined(SYSV_IPC) && !defined(UNIX_DOMAIN_SOCKETS) && \
64     !defined(INTERNET_DOMAIN_SOCKETS)
65 int
66 main(void)
67 {
68         fputs("Sorry, the Emacs server is only supported on systems that have\n\
69 Unix Domain sockets, Internet Domain sockets or System V IPC\n", stderr);
70         return 1;
71 }
72 #else  /* SYSV_IPC || UNIX_DOMAIN_SOCKETS || INTERNET_DOMAIN_SOCKETS */
73
74 # ifdef SYSV_IPC
75
76 /* ipc message queue id */
77 int ipc_qid = 0;
78 /* watchdog task pid */
79 pid_t ipc_wpid = 0;
80
81 /** ipc_exit
82  * clean up the queue id and queue, then kill the watchdog task
83  * if it exists. exit with the given status. **/
84 static void
85 __attribute__((noreturn))
86 ipc_exit(int stat)
87 {
88         msgctl(ipc_qid, IPC_RMID, 0);
89
90         if (ipc_wpid != 0) {
91                 kill(ipc_wpid, SIGKILL);
92         }
93         exit(stat);
94 }
95
96 /** ipc_handle_signal
97  * catch the signal given and clean up. **/
98 static void
99 ipc_handle_signal(int sig)
100 {
101         ipc_exit(0);
102 }
103
104 /** ipc_spawn_watchdog
105  * spawn a watchdog task to clean up the message queue should the
106  * server process die. **/
107 static void
108 ipc_spawn_watchdog(void)
109 {
110         /* child process */
111         if ((ipc_wpid = fork()) == 0) {
112                 /* parent's process id */
113                 pid_t ppid = getppid();
114
115                 /* gnu kills process group on exit */
116                 setpgrp();
117
118                 while (1) {
119                         /* ppid is no longer valid, parent may have died */
120                         if (kill(ppid, 0) < 0) {
121                                 ipc_exit(0);
122                         }
123                         /* else, have another go later */
124                         sleep(10);
125                 }
126         }
127         return;
128 }
129
130 /** ipc_init
131  * initialize server, setting the global msqid that can be listened on. **/
132 static void
133 ipc_init(struct msgbuf **msgpp)
134 {
135         /* messge key */
136         key_t key;
137         /* pathname for key */
138         char buf[GSERV_BUFSZ];
139         int sz;
140
141         SNPRINTF(sz, buf, sizeof(buf),"%s/gsrv%d", tmpdir, (int)geteuid());
142         creat(buf, 0600);
143         key = ftok(buf, 1);
144
145         if ((ipc_qid = msgget(key, 0600 | IPC_CREAT)) == -1) {
146                 perror(progname);
147                 fprintf(stderr, "%s: unable to create msg queue\n", progname);
148                 ipc_exit(1);
149         }
150         ipc_spawn_watchdog();
151
152         signal(SIGTERM, ipc_handle_signal);
153         signal(SIGINT, ipc_handle_signal);
154
155         *msgpp = (struct msgbuf*)malloc(sizeof **msgpp + GSERV_BUFSZ);
156         if (*msgpp == NULL) {
157                 fprintf(stderr,
158                         "%s: unable to allocate space for message buffer\n",
159                         progname);
160                 ipc_exit(1);
161         }
162         return;
163 }
164
165 /** handle_ipc_request
166  * accept a request from a client, pass the request on to the GNU Emacs process,
167  * then wait for its reply and pass that on to the client. **/
168 static void
169 handle_ipc_request(struct msgbuf *msgp)
170 {
171         /* message status */
172         struct msqid_ds msg_st;
173         char buf[GSERV_BUFSZ];
174         /* length of message / read */
175         ssize_t len;
176         /* tag fields on the response from emacs */
177         int s, result_len;
178         int offset = 0;
179         /* # bytes that will actually be sent off */
180         int total = 1;
181
182         if ((len = msgrcv(ipc_qid, msgp, GSERV_BUFSZ - 1, 1, 0)) < 0) {
183                 perror(progname);