1 /* $OpenBSD: client.c,v 1.38 2010/05/04 17:28:16 nicm 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 <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> 34 35 #include "tmux.h" 36 37 struct imsgbuf client_ibuf; 38 struct event client_event; 39 const char *client_exitmsg; 40 int client_exitval; 41 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); 49 50 struct imsgbuf * 51 client_init(char *path, int cmdflags, int flags) 52 { 53 struct sockaddr_un sa; 54 size_t size; 55 int fd, mode; 56 char rpathbuf[MAXPATHLEN]; 57 58 if (realpath(path, rpathbuf) == NULL) 59 strlcpy(rpathbuf, path, sizeof rpathbuf); 60 setproctitle("client (%s)", rpathbuf); 61 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; 68 } 69 70 if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) 71 fatal("socket failed"); 72 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; 85 } 86 goto not_found; 87 } 88 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); 97 98 if (cmdflags & CMD_SENDENVIRON) 99 client_send_environ(); 100 if (isatty(STDIN_FILENO)) 101 client_send_identify(flags); 102 103 return (&client_ibuf); 104 105 start_failed: 106 log_warnx("server failed to start"); 107 return (NULL); 108 109 not_found: 110 log_warn("server not found"); 111 return (NULL); 112 } 113 114 void 115 client_send_identify(int flags) 116 { 117 struct msg_identify_data data; 118 struct winsize ws; 119 char *term; 120 int fd; 121 122 if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) == -1) 123 fatal("ioctl(TIOCGWINSZ)"); 124 data.flags = flags; 125 126 if (getcwd(data.cwd, sizeof data.cwd) == NULL) 127 *data.cwd = '\0'; 128 129 term = getenv("TERM"); 130 if (term == NULL || 131 strlcpy(data.term, term, sizeof data.term) >= sizeof data.term) 132 *data.term = '\0'; 133 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); 138 } 139 140 void 141 client_send_environ(void) 142 { 143 struct msg_environ_data data; 144 char **var; 145 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); 150 } 151 } 152 153 void 154 client_write_server(enum msgtype type, void *buf, size_t len) 155 { 156 imsg_compose(&client_ibuf, type, PROTOCOL_VERSION, -1, -1, buf, len); 157 } 158 159 void 160 client_update_event(void) 161 { 162 short events; 163 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); 170 } 171 172 __dead void 173 client_main(void) 174 { 175 logfile("client"); 176 177 /* Note: event_init() has already been called. */ 178 179 /* Set up signals. */ 180 set_signals(client_signal); 181 182 /* 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. 187 */ 188 if (client_dispatch() != 0) 189 goto out; 190 191 /* Set the event and dispatch. */ 192 client_update_event(); 193 event_dispatch(); 194 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); 200 } 201 202 /* ARGSUSED */ 203 void 204 client_signal(int sig, unused short events, unused void *data) 205 { 206 struct sigaction sigact; 207 208 switch (sig) { 209 case SIGTERM: 210 client_exitmsg = "terminated"; 211 client_exitval = 1; 212 client_write_server(MSG_EXITING, NULL, 0); 213 break; 214 case SIGWINCH: 215 client_write_server(MSG_RESIZE, NULL, 0); 216 break; 217 case SIGCONT: 218 memset(&sigact, 0, sizeof sigact); 219 sigemptyset(&sigact.sa_mask); 220 sigact.sa_flags = SA_RESTART; 221 sigact.sa_handler = SIG_IGN; 222 if (sigaction(SIGTSTP, &sigact, NULL) != 0) 223 fatal("sigaction failed"); 224 client_write_server(MSG_WAKEUP, NULL, 0); 225 break; 226 } 227 228 client_update_event(); 229 } 230 231 /* ARGSUSED */ 232 void 233 client_callback(unused int fd, short events, unused void *data) 234 { 235 ssize_t n; 236 237 if (events & EV_READ) { 238 if ((n = imsg_read(&client_ibuf)) == -1 || n == 0) 239 goto lost_server; 240 if (client_dispatch() != 0) { 241 event_loopexit(NULL); 242 return; 243 } 244 } 245 246 if (events & EV_WRITE) { 247 if (msgbuf_write(&client_ibuf.w) < 0) 248 goto lost_server; 249 } 250 251 client_update_event(); 252 return; 253 254 lost_server: 255 client_exitmsg = "lost server"; 256 client_exitval = 1; 257 event_loopexit(NULL); 258 } 259 260 int 261 client_dispatch(void) 262 { 263 struct imsg imsg; 264 struct msg_lock_data lockdata; 265 struct sigaction sigact; 266 ssize_t n, datalen; 267 268 for (;;) { 269 if ((n = imsg_get(&client_ibuf, &imsg)) == -1) 270 fatalx("imsg_get failed"); 271 if (n == 0) 272 return (0); 273 datalen = imsg.hdr.len - IMSG_HEADER_SIZE; 274 275 log_debug("client got %d", imsg.hdr.type); 276 switch (imsg.hdr.type) { 277 case MSG_DETACH: 278 if (datalen != 0) 279 fatalx("bad MSG_DETACH size"); 280 281 client_write_server(MSG_EXITING, NULL, 0); 282 client_exitmsg = "detached"; 283 break; 284 case MSG_EXIT: 285 if (datalen != 0) 286 fatalx("bad MSG_EXIT size"); 287 288 client_write_server(MSG_EXITING, NULL, 0); 289 client_exitmsg = "exited"; 290 break; 291 case MSG_EXITED: 292 if (datalen != 0) 293 fatalx("bad MSG_EXITED size"); 294 295 imsg_free(&imsg); 296 return (-1); 297 case MSG_SHUTDOWN: 298 if (datalen != 0) 299 fatalx("bad MSG_SHUTDOWN size"); 300 301 client_write_server(MSG_EXITING, NULL, 0); 302 client_exitmsg = "server exited"; 303 client_exitval = 1; 304 break; 305 case MSG_SUSPEND: 306 if (datalen != 0) 307 fatalx("bad MSG_SUSPEND size"); 308 309 memset(&sigact, 0, sizeof sigact); 310 sigemptyset(&sigact.sa_mask); 311 sigact.sa_flags = SA_RESTART; 312 sigact.sa_handler = SIG_DFL; 313 if (sigaction(SIGTSTP, &sigact, NULL) != 0) 314 fatal("sigaction failed"); 315 kill(getpid(), SIGTSTP); 316 break; 317 case MSG_LOCK: 318 if (datalen != sizeof lockdata) 319 fatalx("bad MSG_LOCK size"); 320 memcpy(&lockdata, imsg.data, sizeof lockdata); 321 322 lockdata.cmd[(sizeof lockdata.cmd) - 1] = '\0'; 323 system(lockdata.cmd); 324 client_write_server(MSG_UNLOCK, NULL, 0); 325 break; 326 default: 327 fatalx("unexpected message"); 328 } 329 330 imsg_free(&imsg); 331 } 332 } 333