Rename some imsg bits to make namespace collisions less likely buf to
[tmux-openbsd.git] / client.c
blob092ae555946512a91e66f23cb10483da4871f1ab
1 /* $OpenBSD$ */
3 /*
4 * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
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.
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.
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>
26 #include <errno.h>
27 #include <event.h>
28 #include <fcntl.h>
29 #include <pwd.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <syslog.h>
33 #include <unistd.h>
35 #include "tmux.h"
37 struct imsgbuf client_ibuf;
38 struct event client_event;
39 const char *client_exitmsg;
40 int client_exitval;
42 void client_send_identify(int);
43 void client_send_environ(void);
44 void client_write_server(enum msgtype, void *, size_t);
45 void client_update_event(void);
46 void client_signal(int, short, void *);
47 void client_callback(int, short, void *);
48 int client_dispatch(void);
50 struct imsgbuf *
51 client_init(char *path, int cmdflags, int flags)
53 struct sockaddr_un sa;
54 size_t size;
55 int fd, mode;
56 char rpathbuf[MAXPATHLEN];
58 if (realpath(path, rpathbuf) == NULL)
59 strlcpy(rpathbuf, path, sizeof rpathbuf);
60 setproctitle("client (%s)", rpathbuf);
62 memset(&sa, 0, sizeof sa);
63 sa.sun_family = AF_UNIX;
64 size = strlcpy(sa.sun_path, path, sizeof sa.sun_path);
65 if (size >= sizeof sa.sun_path) {
66 errno = ENAMETOOLONG;
67 goto not_found;
70 if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
71 fatal("socket failed");
73 if (connect(fd, (struct sockaddr *) &sa, SUN_LEN(&sa)) == -1) {
74 if (!(cmdflags & CMD_STARTSERVER))
75 goto not_found;
76 switch (errno) {
77 case ECONNREFUSED:
78 if (unlink(path) != 0)
79 goto not_found;
80 /* FALLTHROUGH */
81 case ENOENT:
82 if ((fd = server_start(path)) == -1)
83 goto start_failed;
84 goto server_started;
86 goto not_found;
89 server_started:
90 if ((mode = fcntl(fd, F_GETFL)) == -1)
91 fatal("fcntl failed");
92 if (fcntl(fd, F_SETFL, mode|O_NONBLOCK) == -1)
93 fatal("fcntl failed");
94 if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1)
95 fatal("fcntl failed");
96 imsg_init(&client_ibuf, fd);
98 if (cmdflags & CMD_SENDENVIRON)
99 client_send_environ();
100 if (isatty(STDIN_FILENO))
101 client_send_identify(flags);
103 return (&client_ibuf);
105 start_failed:
106 log_warnx("server failed to start");
107 return (NULL);
109 not_found:
110 log_warn("server not found");
111 return (NULL);
114 void
115 client_send_identify(int flags)
117 struct msg_identify_data data;
118 struct winsize ws;
119 char *term;
120 int fd;
122 if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) == -1)
123 fatal("ioctl(TIOCGWINSZ)");
124 data.flags = flags;
126 if (getcwd(data.cwd, sizeof data.cwd) == NULL)
127 *data.cwd = '\0';
129 term = getenv("TERM");
130 if (term == NULL ||
131 strlcpy(data.term, term, sizeof data.term) >= sizeof data.term)
132 *data.term = '\0';
134 if ((fd = dup(STDIN_FILENO)) == -1)
135 fatal("dup failed");
136 imsg_compose(&client_ibuf,
137 MSG_IDENTIFY, PROTOCOL_VERSION, -1, fd, &data, sizeof data);
140 void
141 client_send_environ(void)
143 struct msg_environ_data data;
144 char **var;
146 for (var = environ; *var != NULL; var++) {
147 if (strlcpy(data.var, *var, sizeof data.var) >= sizeof data.var)
148 continue;
149 client_write_server(MSG_ENVIRON, &data, sizeof data);
153 void
154 client_write_server(enum msgtype type, void *buf, size_t len)
156 imsg_compose(&client_ibuf, type, PROTOCOL_VERSION, -1, -1, buf, len);
159 void
160 client_update_event(void)
162 short events;
164 event_del(&client_event);
165 events = EV_READ;
166 if (client_ibuf.w.queued > 0)
167 events |= EV_WRITE;
168 event_set(&client_event, client_ibuf.fd, events, client_callback, NULL);
169 event_add(&client_event, NULL);
172 __dead void
173 client_main(void)
175 logfile("client");
177 /* Note: event_init() has already been called. */
179 /* Set up signals. */
180 set_signals(client_signal);
183 * imsg_read in the first client poll loop (before the terminal has
184 * been initialised) may have read messages into the buffer after the
185 * MSG_READY switched to here. Process anything outstanding now to
186 * avoid hanging waiting for messages that have already arrived.
188 if (client_dispatch() != 0)
189 goto out;
191 /* Set the event and dispatch. */
192 client_update_event();
193 event_dispatch();
195 out:
196 /* Print the exit message, if any, and exit. */
197 if (client_exitmsg != NULL && !login_shell)
198 printf("[%s]\n", client_exitmsg);
199 exit(client_exitval);
202 /* ARGSUSED */
203 void
204 client_signal(int sig, unused short events, unused void *data)
206 struct sigaction sigact;
208 switch (sig) {
209 case SIGHUP:
210 client_exitmsg = "lost tty";
211 client_exitval = 1;
212 client_write_server(MSG_EXITING, NULL, 0);
213 break;
214 case SIGTERM:
215 client_exitmsg = "terminated";
216 client_exitval = 1;
217 client_write_server(MSG_EXITING, NULL, 0);
218 break;
219 case SIGWINCH:
220 client_write_server(MSG_RESIZE, NULL, 0);
221 break;
222 case SIGCONT:
223 memset(&sigact, 0, sizeof sigact);
224 sigemptyset(&sigact.sa_mask);
225 sigact.sa_flags = SA_RESTART;
226 sigact.sa_handler = SIG_IGN;
227 if (sigaction(SIGTSTP, &sigact, NULL) != 0)
228 fatal("sigaction failed");
229 client_write_server(MSG_WAKEUP, NULL, 0);
230 break;
233 client_update_event();
236 /* ARGSUSED */
237 void
238 client_callback(unused int fd, short events, unused void *data)
240 ssize_t n;
242 if (events & EV_READ) {
243 if ((n = imsg_read(&client_ibuf)) == -1 || n == 0)
244 goto lost_server;
245 if (client_dispatch() != 0) {
246 event_loopexit(NULL);
247 return;
251 if (events & EV_WRITE) {
252 if (msgbuf_write(&client_ibuf.w) < 0)
253 goto lost_server;
256 client_update_event();
257 return;
259 lost_server:
260 client_exitmsg = "lost server";
261 client_exitval = 1;
262 event_loopexit(NULL);
266 client_dispatch(void)
268 struct imsg imsg;
269 struct msg_lock_data lockdata;
270 struct sigaction sigact;
271 ssize_t n, datalen;
273 for (;;) {
274 if ((n = imsg_get(&client_ibuf, &imsg)) == -1)
275 fatalx("imsg_get failed");
276 if (n == 0)
277 return (0);
278 datalen = imsg.hdr.len - IMSG_HEADER_SIZE;
280 log_debug("client got %d", imsg.hdr.type);
281 switch (imsg.hdr.type) {
282 case MSG_DETACH:
283 if (datalen != 0)
284 fatalx("bad MSG_DETACH size");
286 client_write_server(MSG_EXITING, NULL, 0);
287 client_exitmsg = "detached";
288 break;
289 case MSG_EXIT:
290 if (datalen != 0)
291 fatalx("bad MSG_EXIT size");
293 client_write_server(MSG_EXITING, NULL, 0);
294 client_exitmsg = "exited";
295 break;
296 case MSG_EXITED:
297 if (datalen != 0)
298 fatalx("bad MSG_EXITED size");
300 imsg_free(&imsg);
301 return (-1);
302 case MSG_SHUTDOWN:
303 if (datalen != 0)
304 fatalx("bad MSG_SHUTDOWN size");
306 client_write_server(MSG_EXITING, NULL, 0);
307 client_exitmsg = "server exited";
308 client_exitval = 1;
309 break;
310 case MSG_SUSPEND:
311 if (datalen != 0)
312 fatalx("bad MSG_SUSPEND size");
314 memset(&sigact, 0, sizeof sigact);
315 sigemptyset(&sigact.sa_mask);
316 sigact.sa_flags = SA_RESTART;
317 sigact.sa_handler = SIG_DFL;
318 if (sigaction(SIGTSTP, &sigact, NULL) != 0)
319 fatal("sigaction failed");
320 kill(getpid(), SIGTSTP);
321 break;
322 case MSG_LOCK:
323 if (datalen != sizeof lockdata)
324 fatalx("bad MSG_LOCK size");
325 memcpy(&lockdata, imsg.data, sizeof lockdata);
327 lockdata.cmd[(sizeof lockdata.cmd) - 1] = '\0';
328 system(lockdata.cmd);
329 client_write_server(MSG_UNLOCK, NULL, 0);
330 break;
331 default:
332 fatalx("unexpected message");
335 imsg_free(&imsg);