1 /* $OpenBSD: client.c,v 1.27 2009/10/26 21:38:18 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 <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 struct stat sb; 50 size_t size; 51 int fd, mode; 52 char rpathbuf[MAXPATHLEN]; 53 54 if (realpath(path, rpathbuf) == NULL) 55 strlcpy(rpathbuf, path, sizeof rpathbuf); 56 setproctitle("client (%s)", rpathbuf); 57 58 if (lstat(path, &sb) != 0) { 59 if (cmdflags & CMD_STARTSERVER && errno == ENOENT) { 60 if ((fd = server_start(path)) == -1) 61 goto start_failed; 62 goto server_started; 63 } 64 goto not_found; 65 } 66 if (!S_ISSOCK(sb.st_mode)) { 67 errno = ENOTSOCK; 68 goto not_found; 69 } 70 71 memset(&sa, 0, sizeof sa); 72 sa.sun_family = AF_UNIX; 73 size = strlcpy(sa.sun_path, path, sizeof sa.sun_path); 74 if (size >= sizeof sa.sun_path) { 75 errno = ENAMETOOLONG; 76 goto not_found; 77 } 78 79 if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) 80 fatal("socket failed"); 81 82 if (connect(fd, (struct sockaddr *) &sa, SUN_LEN(&sa)) == -1) { 83 if (errno == ECONNREFUSED) { 84 if (unlink(path) != 0 || !(cmdflags & CMD_STARTSERVER)) 85 goto not_found; 86 if ((fd = server_start(path)) == -1) 87 goto start_failed; 88 goto server_started; 89 } 90 goto not_found; 91 } 92 93 server_started: 94 if ((mode = fcntl(fd, F_GETFL)) == -1) 95 fatal("fcntl failed"); 96 if (fcntl(fd, F_SETFL, mode|O_NONBLOCK) == -1) 97 fatal("fcntl failed"); 98 if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) 99 fatal("fcntl failed"); 100 imsg_init(&client_ibuf, fd); 101 102 if (cmdflags & CMD_SENDENVIRON) 103 client_send_environ(); 104 if (isatty(STDIN_FILENO)) 105 client_send_identify(flags); 106 107 return (&client_ibuf); 108 109 start_failed: 110 log_warnx("server failed to start"); 111 return (NULL); 112 113 not_found: 114 log_warn("server not found"); 115 return (NULL); 116 } 117 118 void 119 client_send_identify(int flags) 120 { 121 struct msg_identify_data data; 122 struct winsize ws; 123 char *term; 124 int fd; 125 126 if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) == -1) 127 fatal("ioctl(TIOCGWINSZ)"); 128 data.flags = flags; 129 130 if (getcwd(data.cwd, sizeof data.cwd) == NULL) 131 *data.cwd = '\0'; 132 133 term = getenv("TERM"); 134 if (term == NULL || 135 strlcpy(data.term, term, sizeof data.term) >= sizeof data.term) 136 *data.term = '\0'; 137 138 if ((fd = dup(STDIN_FILENO)) == -1) 139 fatal("dup failed"); 140 imsg_compose(&client_ibuf, 141 MSG_IDENTIFY, PROTOCOL_VERSION, -1, fd, &data, sizeof data); 142 } 143 144 void 145 client_send_environ(void) 146 { 147 struct msg_environ_data data; 148 char **var; 149 150 for (var = environ; *var != NULL; var++) { 151 if (strlcpy(data.var, *var, sizeof data.var) >= sizeof data.var) 152 continue; 153 client_write_server(MSG_ENVIRON, &data, sizeof data); 154 } 155 } 156 157 void 158 client_write_server(enum msgtype type, void *buf, size_t len) 159 { 160 imsg_compose(&client_ibuf, type, PROTOCOL_VERSION, -1, -1, buf, len); 161 } 162 163 __dead void 164 client_main(void) 165 { 166 struct pollfd pfd; 167 int n, nfds; 168 169 siginit(); 170 171 logfile("client"); 172 173 /* 174 * imsg_read in the first client poll loop (before the terminal has 175 * been initialiased) may have read messages into the buffer after the 176 * MSG_READY switched to here. Process anything outstanding now so poll 177 * doesn't hang waiting for messages that have already arrived. 178 */ 179 if (client_dispatch() != 0) 180 goto out; 181 182 for (;;) { 183 if (sigterm) { 184 client_exitmsg = "terminated"; 185 client_write_server(MSG_EXITING, NULL, 0); 186 } 187 if (sigchld) { 188 sigchld = 0; 189 waitpid(WAIT_ANY, NULL, WNOHANG); 190 continue; 191 } 192 if (sigwinch) { 193 sigwinch = 0; 194 client_write_server(MSG_RESIZE, NULL, 0); 195 continue; 196 } 197 if (sigcont) { 198 sigcont = 0; 199 siginit(); 200 client_write_server(MSG_WAKEUP, NULL, 0); 201 continue; 202 } 203 204 pfd.fd = client_ibuf.fd; 205 pfd.events = POLLIN; 206 if (client_ibuf.w.queued > 0) 207 pfd.events |= POLLOUT; 208 209 if ((nfds = poll(&pfd, 1, INFTIM)) == -1) { 210 if (errno == EAGAIN || errno == EINTR) 211 continue; 212 fatal("poll failed"); 213 } 214 if (nfds == 0) 215 continue; 216 217 if (pfd.revents & (POLLERR|POLLHUP|POLLNVAL)) 218 fatalx("socket error"); 219 220 if (pfd.revents & POLLIN) { 221 if ((n = imsg_read(&client_ibuf)) == -1 || n == 0) { 222 client_exitmsg = "lost server"; 223 break; 224 } 225 if (client_dispatch() != 0) 226 break; 227 } 228 229 if (pfd.revents & POLLOUT) { 230 if (msgbuf_write(&client_ibuf.w) < 0) { 231 client_exitmsg = "lost server"; 232 break; 233 } 234 } 235 } 236 237 out: 238 /* Print the exit message, if any, and exit. */ 239 if (client_exitmsg != NULL) { 240 if (!login_shell) 241 printf("[%s]\n", client_exitmsg); 242 exit(1); 243 } 244 exit(0); 245 } 246 247 int 248 client_dispatch(void) 249 { 250 struct imsg imsg; 251 struct msg_lock_data lockdata; 252 ssize_t n, datalen; 253 254 for (;;) { 255 if ((n = imsg_get(&client_ibuf, &imsg)) == -1) 256 fatalx("imsg_get failed"); 257 if (n == 0) 258 return (0); 259 datalen = imsg.hdr.len - IMSG_HEADER_SIZE; 260 261 switch (imsg.hdr.type) { 262 case MSG_DETACH: 263 if (datalen != 0) 264 fatalx("bad MSG_DETACH size"); 265 266 client_write_server(MSG_EXITING, NULL, 0); 267 client_exitmsg = "detached"; 268 break; 269 case MSG_EXIT: 270 if (datalen != 0) 271 fatalx("bad MSG_EXIT size"); 272 273 client_write_server(MSG_EXITING, NULL, 0); 274 client_exitmsg = "exited"; 275 break; 276 case MSG_EXITED: 277 if (datalen != 0) 278 fatalx("bad MSG_EXITED size"); 279 280 imsg_free(&imsg); 281 return (-1); 282 case MSG_SHUTDOWN: 283 if (datalen != 0) 284 fatalx("bad MSG_SHUTDOWN size"); 285 286 client_write_server(MSG_EXITING, NULL, 0); 287 client_exitmsg = "server exited"; 288 break; 289 case MSG_SUSPEND: 290 if (datalen != 0) 291 fatalx("bad MSG_SUSPEND size"); 292 293 client_suspend(); 294 break; 295 case MSG_LOCK: 296 if (datalen != sizeof lockdata) 297 fatalx("bad MSG_LOCK size"); 298 memcpy(&lockdata, imsg.data, sizeof lockdata); 299 300 lockdata.cmd[(sizeof lockdata.cmd) - 1] = '\0'; 301 system(lockdata.cmd); 302 client_write_server(MSG_UNLOCK, NULL, 0); 303 break; 304 default: 305 fatalx("unexpected message"); 306 } 307 308 imsg_free(&imsg); 309 } 310 } 311 312 void 313 client_suspend(void) 314 { 315 struct sigaction act; 316 317 memset(&act, 0, sizeof act); 318 sigemptyset(&act.sa_mask); 319 act.sa_flags = SA_RESTART; 320 321 act.sa_handler = SIG_DFL; 322 if (sigaction(SIGTSTP, &act, NULL) != 0) 323 fatal("sigaction failed"); 324 325 act.sa_handler = sighandler; 326 if (sigaction(SIGCONT, &act, NULL) != 0) 327 fatal("sigaction failed"); 328 329 kill(getpid(), SIGTSTP); 330 } 331