Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 326 lines (275 sloc) 7.315 kb
8fade26 Imported Upstream version 1.1
Karl Ferdinand Ebert authored
1 /* $Id: client.c,v 1.84 2009/11/02 21:41:16 tcunha Exp $ */
2
3 /*
4 * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19 #include <sys/types.h>
20 #include <sys/ioctl.h>
21 #include <sys/socket.h>
22 #include <sys/stat.h>
23 #include <sys/un.h>
24 #include <sys/wait.h>
25
26 #include <errno.h>
27 #include <fcntl.h>
28 #include <pwd.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <syslog.h>
32 #include <unistd.h>
33
34 #include "tmux.h"
35
36 struct imsgbuf client_ibuf;
37 const char *client_exitmsg;
38
39 void client_send_identify(int);
40 void client_send_environ(void);
41 void client_write_server(enum msgtype, void *, size_t);
42 int client_dispatch(void);
43 void client_suspend(void);
44
45 struct imsgbuf *
46 client_init(char *path, int cmdflags, int flags)
47 {
48 struct sockaddr_un sa;
49 size_t size;
50 int fd, mode;
51 #ifdef HAVE_SETPROCTITLE
52 char rpathbuf[MAXPATHLEN];
53 #endif
54
55 #ifdef HAVE_SETPROCTITLE
56 if (realpath(path, rpathbuf) == NULL)
57 strlcpy(rpathbuf, path, sizeof rpathbuf);
58 setproctitle("client (%s)", rpathbuf);
59 #endif
60
61 memset(&sa, 0, sizeof sa);
62 sa.sun_family = AF_UNIX;
63 size = strlcpy(sa.sun_path, path, sizeof sa.sun_path);
64 if (size >= sizeof sa.sun_path) {
65 errno = ENAMETOOLONG;
66 goto not_found;
67 }
68
69 if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
70 fatal("socket failed");
71
72 if (connect(fd, (struct sockaddr *) &sa, SUN_LEN(&sa)) == -1) {
73 if (!(cmdflags & CMD_STARTSERVER))
74 goto not_found;
75 switch (errno) {
76 case ECONNREFUSED:
77 if (unlink(path) != 0)
78 goto not_found;
79 /* FALLTHROUGH */
80 case ENOENT:
81 if ((fd = server_start(path)) == -1)
82 goto start_failed;
83 goto server_started;
84 }
85 goto not_found;
86 }
87
88 server_started:
89 if ((mode = fcntl(fd, F_GETFL)) == -1)
90 fatal("fcntl failed");
91 if (fcntl(fd, F_SETFL, mode|O_NONBLOCK) == -1)
92 fatal("fcntl failed");
93 if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1)
94 fatal("fcntl failed");
95 imsg_init(&client_ibuf, fd);
96
97 if (cmdflags & CMD_SENDENVIRON)
98 client_send_environ();
99 if (isatty(STDIN_FILENO))
100 client_send_identify(flags);
101
102 return (&client_ibuf);
103
104 start_failed:
105 log_warnx("server failed to start");
106 return (NULL);
107
108 not_found:
109 log_warn("server not found");
110 return (NULL);
111 }
112
113 void
114 client_send_identify(int flags)
115 {
116 struct msg_identify_data data;
117 struct winsize ws;
118 char *term;
119 int fd;
120
121 if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) == -1)
122 fatal("ioctl(TIOCGWINSZ)");
123 data.flags = flags;
124
125 if (getcwd(data.cwd, sizeof data.cwd) == NULL)
126 *data.cwd = '\0';
127
128 term = getenv("TERM");
129 if (term == NULL ||
130 strlcpy(data.term, term, sizeof data.term) >= sizeof data.term)
131 *data.term = '\0';
132
133 if ((fd = dup(STDIN_FILENO)) == -1)
134 fatal("dup failed");
135 imsg_compose(&client_ibuf,
136 MSG_IDENTIFY, PROTOCOL_VERSION, -1, fd, &data, sizeof data);
137 }
138
139 void
140 client_send_environ(void)
141 {
142 struct msg_environ_data data;
143 char **var;
144
145 for (var = environ; *var != NULL; var++) {
146 if (strlcpy(data.var, *var, sizeof data.var) >= sizeof data.var)
147 continue;
148 client_write_server(MSG_ENVIRON, &data, sizeof data);
149 }
150 }
151
152 void
153 client_write_server(enum msgtype type, void *buf, size_t len)
154 {
155 imsg_compose(&client_ibuf, type, PROTOCOL_VERSION, -1, -1, buf, len);
156 }
157
158 __dead void
159 client_main(void)
160 {
161 struct pollfd pfd;
162 int n, nfds;
163
164 siginit();
165
166 logfile("client");
167
168 /*
169 * imsg_read in the first client poll loop (before the terminal has
170 * been initialiased) may have read messages into the buffer after the
171 * MSG_READY switched to here. Process anything outstanding now so poll
172 * doesn't hang waiting for messages that have already arrived.
173 */
174 if (client_dispatch() != 0)
175 goto out;
176
177 for (;;) {
178 if (sigterm) {
179 client_exitmsg = "terminated";
180 client_write_server(MSG_EXITING, NULL, 0);
181 }
182 if (sigchld) {
183 sigchld = 0;
184 waitpid(WAIT_ANY, NULL, WNOHANG);
185 continue;
186 }
187 if (sigwinch) {
188 sigwinch = 0;
189 client_write_server(MSG_RESIZE, NULL, 0);
190 continue;
191 }
192 if (sigcont) {
193 sigcont = 0;
194 siginit();
195 client_write_server(MSG_WAKEUP, NULL, 0);
196 continue;
197 }
198
199 pfd.fd = client_ibuf.fd;
200 pfd.events = POLLIN;
201 if (client_ibuf.w.queued > 0)
202 pfd.events |= POLLOUT;
203
204 if ((nfds = poll(&pfd, 1, INFTIM)) == -1) {
205 if (errno == EAGAIN || errno == EINTR)
206 continue;
207 fatal("poll failed");
208 }
209 if (nfds == 0)
210 continue;
211
212 if (pfd.revents & (POLLERR|POLLHUP|POLLNVAL))
213 fatalx("socket error");
214
215 if (pfd.revents & POLLIN) {
216 if ((n = imsg_read(&client_ibuf)) == -1 || n == 0) {
217 client_exitmsg = "lost server";
218 break;
219 }
220 if (client_dispatch() != 0)
221 break;
222 }
223
224 if (pfd.revents & POLLOUT) {
225 if (msgbuf_write(&client_ibuf.w) < 0) {
226 client_exitmsg = "lost server";
227 break;
228 }
229 }
230 }
231
232 out:
233 /* Print the exit message, if any, and exit. */
234 if (client_exitmsg != NULL) {
235 if (!login_shell)
236 printf("[%s]\n", client_exitmsg);
237 exit(1);
238 }
239 exit(0);
240 }
241
242 int
243 client_dispatch(void)
244 {
245 struct imsg imsg;
246 struct msg_lock_data lockdata;
247 ssize_t n, datalen;
248
249 for (;;) {
250 if ((n = imsg_get(&client_ibuf, &imsg)) == -1)
251 fatalx("imsg_get failed");
252 if (n == 0)
253 return (0);
254 datalen = imsg.hdr.len - IMSG_HEADER_SIZE;
255
256 switch (imsg.hdr.type) {
257 case MSG_DETACH:
258 if (datalen != 0)
259 fatalx("bad MSG_DETACH size");
260
261 client_write_server(MSG_EXITING, NULL, 0);
262 client_exitmsg = "detached";
263 break;
264 case MSG_EXIT:
265 if (datalen != 0)
266 fatalx("bad MSG_EXIT size");
267
268 client_write_server(MSG_EXITING, NULL, 0);
269 client_exitmsg = "exited";
270 break;
271 case MSG_EXITED:
272 if (datalen != 0)
273 fatalx("bad MSG_EXITED size");
274
275 imsg_free(&imsg);
276 return (-1);
277 case MSG_SHUTDOWN:
278 if (datalen != 0)
279 fatalx("bad MSG_SHUTDOWN size");
280
281 client_write_server(MSG_EXITING, NULL, 0);
282 client_exitmsg = "server exited";
283 break;
284 case MSG_SUSPEND:
285 if (datalen != 0)
286 fatalx("bad MSG_SUSPEND size");
287
288 client_suspend();
289 break;
290 case MSG_LOCK:
291 if (datalen != sizeof lockdata)
292 fatalx("bad MSG_LOCK size");
293 memcpy(&lockdata, imsg.data, sizeof lockdata);
294
295 lockdata.cmd[(sizeof lockdata.cmd) - 1] = '\0';
296 system(lockdata.cmd);
297 client_write_server(MSG_UNLOCK, NULL, 0);
298 break;
299 default:
300 fatalx("unexpected message");
301 }
302
303 imsg_free(&imsg);
304 }
305 }
306
307 void
308 client_suspend(void)
309 {
310 struct sigaction act;
311
312 memset(&act, 0, sizeof act);
313 sigemptyset(&act.sa_mask);
314 act.sa_flags = SA_RESTART;
315
316 act.sa_handler = SIG_DFL;
317 if (sigaction(SIGTSTP, &act, NULL) != 0)
318 fatal("sigaction failed");
319
320 act.sa_handler = sighandler;
321 if (sigaction(SIGCONT, &act, NULL) != 0)
322 fatal("sigaction failed");
323
324 kill(getpid(), SIGTSTP);
325 }
Something went wrong with that request. Please try again.