GCC Code Coverage Report | |||||||||||||||||||||
|
|||||||||||||||||||||
Line | Branch | Exec | Source |
1 |
/* $OpenBSD: client.c,v 1.123 2017/07/14 18:49:07 nicm Exp $ */ |
||
2 |
|||
3 |
/* |
||
4 |
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com> |
||
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/file.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 <imsg.h> |
||
30 |
#include <signal.h> |
||
31 |
#include <stdlib.h> |
||
32 |
#include <string.h> |
||
33 |
#include <unistd.h> |
||
34 |
|||
35 |
#include "tmux.h" |
||
36 |
|||
37 |
static struct tmuxproc *client_proc; |
||
38 |
static struct tmuxpeer *client_peer; |
||
39 |
static int client_flags; |
||
40 |
static struct event client_stdin; |
||
41 |
static enum { |
||
42 |
CLIENT_EXIT_NONE, |
||
43 |
CLIENT_EXIT_DETACHED, |
||
44 |
CLIENT_EXIT_DETACHED_HUP, |
||
45 |
CLIENT_EXIT_LOST_TTY, |
||
46 |
CLIENT_EXIT_TERMINATED, |
||
47 |
CLIENT_EXIT_LOST_SERVER, |
||
48 |
CLIENT_EXIT_EXITED, |
||
49 |
CLIENT_EXIT_SERVER_EXITED, |
||
50 |
} client_exitreason = CLIENT_EXIT_NONE; |
||
51 |
static int client_exitval; |
||
52 |
static enum msgtype client_exittype; |
||
53 |
static const char *client_exitsession; |
||
54 |
static const char *client_execshell; |
||
55 |
static const char *client_execcmd; |
||
56 |
static int client_attached; |
||
57 |
|||
58 |
static __dead void client_exec(const char *,const char *); |
||
59 |
static int client_get_lock(char *); |
||
60 |
static int client_connect(struct event_base *, const char *, int); |
||
61 |
static void client_send_identify(const char *, const char *); |
||
62 |
static void client_stdin_callback(int, short, void *); |
||
63 |
static void client_write(int, const char *, size_t); |
||
64 |
static void client_signal(int); |
||
65 |
static void client_dispatch(struct imsg *, void *); |
||
66 |
static void client_dispatch_attached(struct imsg *); |
||
67 |
static void client_dispatch_wait(struct imsg *); |
||
68 |
static const char *client_exit_message(void); |
||
69 |
|||
70 |
/* |
||
71 |
* Get server create lock. If already held then server start is happening in |
||
72 |
* another client, so block until the lock is released and return -2 to |
||
73 |
* retry. Return -1 on failure to continue and start the server anyway. |
||
74 |
*/ |
||
75 |
static int |
||
76 |
client_get_lock(char *lockfile) |
||
77 |
{ |
||
78 |
int lockfd; |
||
79 |
|||
80 |
4 |
log_debug("lock file is %s", lockfile); |
|
81 |
|||
82 |
✗✓ | 2 |
if ((lockfd = open(lockfile, O_WRONLY|O_CREAT, 0600)) == -1) { |
83 |
log_debug("open failed: %s", strerror(errno)); |
||
84 |
return (-1); |
||
85 |
} |
||
86 |
|||
87 |
✗✓ | 2 |
if (flock(lockfd, LOCK_EX|LOCK_NB) == -1) { |
88 |
log_debug("flock failed: %s", strerror(errno)); |
||
89 |
if (errno != EAGAIN) |
||
90 |
return (lockfd); |
||
91 |
while (flock(lockfd, LOCK_EX) == -1 && errno == EINTR) |
||
92 |
/* nothing */; |
||
93 |
close(lockfd); |
||
94 |
return (-2); |
||
95 |
} |
||
96 |
2 |
log_debug("flock succeeded"); |
|
97 |
|||
98 |
2 |
return (lockfd); |
|
99 |
2 |
} |
|
100 |
|||
101 |
/* Connect client to server. */ |
||
102 |
static int |
||
103 |
client_connect(struct event_base *base, const char *path, int start_server) |
||
104 |
{ |
||
105 |
10 |
struct sockaddr_un sa; |
|
106 |
size_t size; |
||
107 |
int fd, lockfd = -1, locked = 0; |
||
108 |
5 |
char *lockfile = NULL; |
|
109 |
|||
110 |
5 |
memset(&sa, 0, sizeof sa); |
|
111 |
5 |
sa.sun_family = AF_UNIX; |
|
112 |
5 |
size = strlcpy(sa.sun_path, path, sizeof sa.sun_path); |
|
113 |
✗✓ | 5 |
if (size >= sizeof sa.sun_path) { |
114 |
errno = ENAMETOOLONG; |
||
115 |
return (-1); |
||
116 |
} |
||
117 |
5 |
log_debug("socket is %s", path); |
|
118 |
|||
119 |
retry: |
||
120 |
✗✓ | 7 |
if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) |
121 |
return (-1); |
||
122 |
|||
123 |
7 |
log_debug("trying connect"); |
|
124 |
✓✓ | 7 |
if (connect(fd, (struct sockaddr *)&sa, sizeof sa) == -1) { |
125 |
4 |
log_debug("connect failed: %s", strerror(errno)); |
|
126 |
✗✓✗✗ |
4 |
if (errno != ECONNREFUSED && errno != ENOENT) |
127 |
goto failed; |
||
128 |
✓✗ | 4 |
if (!start_server) |
129 |
goto failed; |
||
130 |
4 |
close(fd); |
|
131 |
|||
132 |
✓✓ | 4 |
if (!locked) { |
133 |
2 |
xasprintf(&lockfile, "%s.lock", path); |
|
134 |
✗✓ | 2 |
if ((lockfd = client_get_lock(lockfile)) < 0) { |
135 |
log_debug("didn't get lock (%d)", lockfd); |
||
136 |
|||
137 |
free(lockfile); |
||
138 |
lockfile = NULL; |
||
139 |
|||
140 |
if (lockfd == -2) |
||
141 |
goto retry; |
||
142 |
} |
||
143 |
2 |
log_debug("got lock (%d)", lockfd); |
|
144 |
|||
145 |
/* |
||
146 |
* Always retry at least once, even if we got the lock, |
||
147 |
* because another client could have taken the lock, |
||
148 |
* started the server and released the lock between our |
||
149 |
* connect() and flock(). |
||
150 |
*/ |
||
151 |
locked = 1; |
||
152 |
2 |
goto retry; |
|
153 |
} |
||
154 |
|||
155 |
✓✗✗✓ ✗✗ |
4 |
if (lockfd >= 0 && unlink(path) != 0 && errno != ENOENT) { |
156 |
free(lockfile); |
||
157 |
close(lockfd); |
||
158 |
return (-1); |
||
159 |
} |
||
160 |
2 |
fd = server_start(client_proc, base, lockfd, lockfile); |
|
161 |
2 |
} |
|
162 |
|||
163 |
✓✓ | 5 |
if (locked && lockfd >= 0) { |
164 |
2 |
free(lockfile); |
|
165 |
2 |
close(lockfd); |
|
166 |
2 |
} |
|
167 |
5 |
setblocking(fd, 0); |
|
168 |
5 |
return (fd); |
|
169 |
|||
170 |
failed: |
||
171 |
if (locked) { |
||
172 |
free(lockfile); |
||
173 |
close(lockfd); |
||
174 |
} |
||
175 |
close(fd); |
||
176 |
return (-1); |
||
177 |
5 |
} |
|
178 |
|||
179 |
/* Get exit string from reason number. */ |
||
180 |
const char * |
||
181 |
client_exit_message(void) |
||
182 |
{ |
||
183 |
static char msg[256]; |
||
184 |
|||
185 |
✓✗✗✗ ✗✓✗✗ |
8 |
switch (client_exitreason) { |
186 |
case CLIENT_EXIT_NONE: |
||
187 |
break; |
||
188 |
case CLIENT_EXIT_DETACHED: |
||
189 |
✓✗ | 1 |
if (client_exitsession != NULL) { |
190 |
1 |
xsnprintf(msg, sizeof msg, "detached " |
|
191 |
"(from session %s)", client_exitsession); |
||
192 |
1 |
return (msg); |
|
193 |
} |
||
194 |
return ("detached"); |
||
195 |
case CLIENT_EXIT_DETACHED_HUP: |
||
196 |
if (client_exitsession != NULL) { |
||
197 |
xsnprintf(msg, sizeof msg, "detached and SIGHUP " |
||
198 |
"(from session %s)", client_exitsession); |
||
199 |
return (msg); |
||
200 |
} |
||
201 |
return ("detached and SIGHUP"); |
||
202 |
case CLIENT_EXIT_LOST_TTY: |
||
203 |
return ("lost tty"); |
||
204 |
case CLIENT_EXIT_TERMINATED: |
||
205 |
return ("terminated"); |
||
206 |
case CLIENT_EXIT_LOST_SERVER: |
||
207 |
return ("lost server"); |
||
208 |
case CLIENT_EXIT_EXITED: |
||
209 |
3 |
return ("exited"); |
|
210 |
case CLIENT_EXIT_SERVER_EXITED: |
||
211 |
return ("server exited"); |
||
212 |
} |
||
213 |
return ("unknown reason"); |
||
214 |
4 |
} |
|
215 |
|||
216 |
/* Client main loop. */ |
||
217 |
int |
||
218 |
client_main(struct event_base *base, int argc, char **argv, int flags) |
||
219 |
{ |
||
220 |
struct cmd *cmd; |
||
221 |
struct cmd_list *cmdlist; |
||
222 |
struct msg_command_data *data; |
||
223 |
int cmdflags, fd, i; |
||
224 |
const char *ttynam, *cwd; |
||
225 |
pid_t ppid; |
||
226 |
enum msgtype msg; |
||
227 |
10 |
char *cause, path[PATH_MAX]; |
|
228 |
5 |
struct termios tio, saved_tio; |
|
229 |
size_t size; |
||
230 |
|||
231 |
/* Ignore SIGCHLD now or daemon() in the server will leave a zombie. */ |
||
232 |
5 |
signal(SIGCHLD, SIG_IGN); |
|
233 |
|||
234 |
/* Save the flags. */ |
||
235 |
5 |
client_flags = flags; |
|
236 |
|||
237 |
/* Set up the initial command. */ |
||
238 |
cmdflags = 0; |
||
239 |
✗✓ | 5 |
if (shell_command != NULL) { |
240 |
msg = MSG_SHELL; |
||
241 |
cmdflags = CMD_STARTSERVER; |
||
242 |
✓✓ | 5 |
} else if (argc == 0) { |
243 |
msg = MSG_COMMAND; |
||
244 |
cmdflags = CMD_STARTSERVER; |
||
245 |
1 |
} else { |
|
246 |
msg = MSG_COMMAND; |
||
247 |
|||
248 |
/* |
||
249 |
* It sucks parsing the command string twice (in client and |
||
250 |
* later in server) but it is necessary to get the start server |
||
251 |
* flag. |
||
252 |
*/ |
||
253 |
4 |
cmdlist = cmd_list_parse(argc, argv, NULL, 0, &cause); |
|
254 |
✓✗ | 4 |
if (cmdlist != NULL) { |
255 |
✓✓ | 16 |
TAILQ_FOREACH(cmd, &cmdlist->list, qentry) { |
256 |
✓✗ | 4 |
if (cmd->entry->flags & CMD_STARTSERVER) |
257 |
4 |
cmdflags |= CMD_STARTSERVER; |
|
258 |
} |
||
259 |
4 |
cmd_list_free(cmdlist); |
|
260 |
4 |
} |
|
261 |
} |
||
262 |
|||
263 |
/* Create client process structure (starts logging). */ |
||
264 |
5 |
client_proc = proc_start("client"); |
|
265 |
5 |
proc_set_signals(client_proc, client_signal); |
|
266 |
|||
267 |
/* Initialize the client socket and start the server. */ |
||
268 |
5 |
fd = client_connect(base, socket_path, cmdflags & CMD_STARTSERVER); |
|
269 |
✗✓ | 5 |
if (fd == -1) { |
270 |
if (errno == ECONNREFUSED) { |
||
271 |
fprintf(stderr, "no server running on %s\n", |
||
272 |
socket_path); |
||
273 |
} else { |
||
274 |
fprintf(stderr, "error connecting to %s (%s)\n", |
||
275 |
socket_path, strerror(errno)); |
||
276 |
} |
||
277 |
return (1); |
||
278 |
} |
||
279 |
5 |
client_peer = proc_add_peer(client_proc, fd, client_dispatch, NULL); |
|
280 |
|||
281 |
/* Save these before pledge(). */ |
||
282 |
✗✓ | 5 |
if ((cwd = getcwd(path, sizeof path)) == NULL) { |
283 |
if ((cwd = find_home()) == NULL) |
||
284 |
cwd = "/"; |
||
285 |
} |
||
286 |
5 |
if ((ttynam = ttyname(STDIN_FILENO)) == NULL) |
|
287 |
ttynam = ""; |
||
288 |
|||
289 |
/* |
||
290 |
* Drop privileges for client. "proc exec" is needed for -c and for |
||
291 |
* locking (which uses system(3)). |
||
292 |
* |
||
293 |
* "tty" is needed to restore termios(4) and also for some reason -CC |
||
294 |
* does not work properly without it (input is not recognised). |
||
295 |
* |
||
296 |
* "sendfd" is dropped later in client_dispatch_wait(). |
||
297 |
*/ |
||
298 |
✗✓ | 5 |
if (pledge("stdio unix sendfd proc exec tty flock rpath cpath wpath", NULL) != 0) |
299 |
fatal("pledge failed"); |
||
300 |
|||
301 |
/* Free stuff that is not used in the client. */ |
||
302 |
✓✗ | 5 |
if (ptm_fd != -1) |
303 |
5 |
close(ptm_fd); |
|
304 |
5 |
options_free(global_options); |
|
305 |
5 |
options_free(global_s_options); |
|
306 |
5 |
options_free(global_w_options); |
|
307 |
5 |
environ_free(global_environ); |
|
308 |
|||
309 |
/* Create stdin handler. */ |
||
310 |
5 |
setblocking(STDIN_FILENO, 0); |
|
311 |
5 |
event_set(&client_stdin, STDIN_FILENO, EV_READ|EV_PERSIST, |
|
312 |
client_stdin_callback, NULL); |
||
313 |
✗✓ | 5 |
if (client_flags & CLIENT_CONTROLCONTROL) { |
314 |
if (tcgetattr(STDIN_FILENO, &saved_tio) != 0) { |
||
315 |
fprintf(stderr, "tcgetattr failed: %s\n", |
||
316 |
strerror(errno)); |
||
317 |
return (1); |
||
318 |
} |
||
319 |
cfmakeraw(&tio); |
||
320 |
tio.c_iflag = ICRNL|IXANY; |
||
321 |
tio.c_oflag = OPOST|ONLCR; |
||
322 |
tio.c_lflag = NOKERNINFO; |
||
323 |
tio.c_cflag = CREAD|CS8|HUPCL; |
||
324 |
tio.c_cc[VMIN] = 1; |
||
325 |
tio.c_cc[VTIME] = 0; |
||
326 |
cfsetispeed(&tio, cfgetispeed(&saved_tio)); |
||
327 |
cfsetospeed(&tio, cfgetospeed(&saved_tio)); |
||
328 |
tcsetattr(STDIN_FILENO, TCSANOW, &tio); |
||
329 |
} |
||
330 |
|||
331 |
/* Send identify messages. */ |
||
332 |
5 |
client_send_identify(ttynam, cwd); |
|
333 |
|||
334 |
/* Send first command. */ |
||
335 |
✓✗ | 5 |
if (msg == MSG_COMMAND) { |
336 |
/* How big is the command? */ |
||
337 |
size = 0; |
||
338 |
✓✓ | 18 |
for (i = 0; i < argc; i++) |
339 |
4 |
size += strlen(argv[i]) + 1; |
|
340 |
5 |
data = xmalloc((sizeof *data) + size); |
|
341 |
|||
342 |
/* Prepare command for server. */ |
||
343 |
5 |
data->argc = argc; |
|
344 |
✗✓ | 5 |
if (cmd_pack_argv(argc, argv, (char *)(data + 1), size) != 0) { |
345 |
fprintf(stderr, "command too long\n"); |
||
346 |
free(data); |
||
347 |
return (1); |
||
348 |
} |
||
349 |
size += sizeof *data; |
||
350 |
|||
351 |
/* Send the command. */ |
||
352 |
✗✓ | 5 |
if (proc_send(client_peer, msg, -1, data, size) != 0) { |
353 |
fprintf(stderr, "failed to send command\n"); |
||
354 |
free(data); |
||
355 |
return (1); |
||
356 |
} |
||
357 |
5 |
free(data); |
|
358 |
✗✗ | 5 |
} else if (msg == MSG_SHELL) |
359 |
proc_send(client_peer, msg, -1, NULL, 0); |
||
360 |
|||
361 |
/* Start main loop. */ |
||
362 |
5 |
proc_loop(client_proc, NULL); |
|
363 |
|||
364 |
/* Run command if user requested exec, instead of exiting. */ |
||
365 |
✗✓ | 5 |
if (client_exittype == MSG_EXEC) { |
366 |
if (client_flags & CLIENT_CONTROLCONTROL) |
||
367 |
tcsetattr(STDOUT_FILENO, TCSAFLUSH, &saved_tio); |
||
368 |
client_exec(client_execshell, client_execcmd); |
||
369 |
} |
||
370 |
|||
371 |
/* Print the exit message, if any, and exit. */ |
||
372 |
✓✓ | 5 |
if (client_attached) { |
373 |
✓✗ | 4 |
if (client_exitreason != CLIENT_EXIT_NONE) |
374 |
4 |
printf("[%s]\n", client_exit_message()); |
|
375 |
|||
376 |
4 |
ppid = getppid(); |
|
377 |
✗✓ | 4 |
if (client_exittype == MSG_DETACHKILL && ppid > 1) |
378 |
kill(ppid, SIGHUP); |
||
379 |
✗✓ | 1 |
} else if (client_flags & CLIENT_CONTROLCONTROL) { |
380 |
if (client_exitreason != CLIENT_EXIT_NONE) |
||
381 |
printf("%%exit %s\n", client_exit_message()); |
||
382 |
else |
||
383 |
printf("%%exit\n"); |
||
384 |
printf("\033\\"); |
||
385 |
tcsetattr(STDOUT_FILENO, TCSAFLUSH, &saved_tio); |
||
386 |
✗✓ | 1 |
} else if (client_exitreason != CLIENT_EXIT_NONE) |
387 |
fprintf(stderr, "%s\n", client_exit_message()); |
||
388 |
5 |
setblocking(STDIN_FILENO, 1); |
|
389 |
5 |
return (client_exitval); |
|
390 |
5 |
} |
|
391 |
|||
392 |
/* Send identify messages to server. */ |
||
393 |
static void |
||
394 |
client_send_identify(const char *ttynam, const char *cwd) |
||
395 |
{ |
||
396 |
const char *s; |
||
397 |
char **ss; |
||
398 |
size_t sslen; |
||
399 |
10 |
int fd, flags = client_flags; |
|
400 |
5 |
pid_t pid; |
|
401 |
|||
402 |
5 |
proc_send(client_peer, MSG_IDENTIFY_FLAGS, -1, &flags, sizeof flags); |
|
403 |
|||
404 |
5 |
if ((s = getenv("TERM")) == NULL) |
|
405 |
s = ""; |
||
406 |
5 |
proc_send(client_peer, MSG_IDENTIFY_TERM, -1, s, strlen(s) + 1); |
|
407 |
|||
408 |
10 |
proc_send(client_peer, MSG_IDENTIFY_TTYNAME, -1, ttynam, |
|
409 |
5 |
strlen(ttynam) + 1); |
|
410 |
5 |
proc_send(client_peer, MSG_IDENTIFY_CWD, -1, cwd, strlen(cwd) + 1); |
|
411 |
|||
412 |
✗✓ | 5 |
if ((fd = dup(STDIN_FILENO)) == -1) |
413 |
fatal("dup failed"); |
||
414 |
5 |
proc_send(client_peer, MSG_IDENTIFY_STDIN, fd, NULL, 0); |
|
415 |
|||
416 |
5 |
pid = getpid(); |
|
417 |
5 |
proc_send(client_peer, MSG_IDENTIFY_CLIENTPID, -1, &pid, sizeof pid); |
|
418 |
|||
419 |
✓✓ | 146 |
for (ss = environ; *ss != NULL; ss++) { |
420 |
68 |
sslen = strlen(*ss) + 1; |
|
421 |
✓✗ | 68 |
if (sslen > MAX_IMSGSIZE - IMSG_HEADER_SIZE) |
422 |
continue; |
||
423 |
68 |
proc_send(client_peer, MSG_IDENTIFY_ENVIRON, -1, *ss, sslen); |
|
424 |
68 |
} |
|
425 |
|||
426 |
5 |
proc_send(client_peer, MSG_IDENTIFY_DONE, -1, NULL, 0); |
|
427 |
5 |
} |
|
428 |
|||
429 |
/* Callback for client stdin read events. */ |
||
430 |
static void |
||
431 |
client_stdin_callback(__unused int fd, __unused short events, |
||
432 |
__unused void *arg) |
||
433 |
{ |
||
434 |
struct msg_stdin_data data; |
||
435 |
|||
436 |
data.size = read(STDIN_FILENO, data.data, sizeof data.data); |
||
437 |
if (data.size < 0 && (errno == EINTR || errno == EAGAIN)) |
||
438 |
return; |
||
439 |
|||
440 |
proc_send(client_peer, MSG_STDIN, -1, &data, sizeof data); |
||
441 |
if (data.size <= 0) |
||
442 |
event_del(&client_stdin); |
||
443 |
} |
||
444 |
|||
445 |
/* Force write to file descriptor. */ |
||
446 |
static void |
||
447 |
client_write(int fd, const char *data, size_t size) |
||
448 |
{ |
||
449 |
ssize_t used; |
||
450 |
|||
451 |
✓✓ | 5 |
while (size != 0) { |
452 |
1 |
used = write(fd, data, size); |
|
453 |
✗✓ | 1 |
if (used == -1) { |
454 |
if (errno == EINTR || errno == EAGAIN) |
||
455 |
continue; |
||
456 |
break; |
||
457 |
} |
||
458 |
1 |
data += used; |
|
459 |
1 |
size -= used; |
|
460 |
} |
||
461 |
1 |
} |
|
462 |
|||
463 |
/* Run command in shell; used for -c. */ |
||
464 |
static __dead void |
||
465 |
client_exec(const char *shell, const char *shellcmd) |
||
466 |
{ |
||
467 |
const char *name, *ptr; |
||
468 |
char *argv0; |
||
469 |
|||
470 |
log_debug("shell %s, command %s", shell, shellcmd); |
||
471 |
|||
472 |
ptr = strrchr(shell, '/'); |
||
473 |
if (ptr != NULL && *(ptr + 1) != '\0') |
||
474 |
name = ptr + 1; |
||
475 |
else |
||
476 |
name = shell; |
||
477 |
if (client_flags & CLIENT_LOGIN) |
||
478 |
xasprintf(&argv0, "-%s", name); |
||
479 |
else |
||
480 |
xasprintf(&argv0, "%s", name); |
||
481 |
setenv("SHELL", shell, 1); |
||
482 |
|||
483 |
proc_clear_signals(client_proc, 1); |
||
484 |
|||
485 |
setblocking(STDIN_FILENO, 1); |
||
486 |
setblocking(STDOUT_FILENO, 1); |
||
487 |
setblocking(STDERR_FILENO, 1); |
||
488 |
closefrom(STDERR_FILENO + 1); |
||
489 |
|||
490 |
execl(shell, argv0, "-c", shellcmd, (char *) NULL); |
||
491 |
fatal("execl failed"); |
||
492 |
} |
||
493 |
|||
494 |
/* Callback to handle signals in the client. */ |
||
495 |
static void |
||
496 |
client_signal(int sig) |
||
497 |
{ |
||
498 |
4 |
struct sigaction sigact; |
|
499 |
2 |
int status; |
|
500 |
|||
501 |
✓✗ | 2 |
if (sig == SIGCHLD) |
502 |
2 |
waitpid(WAIT_ANY, &status, WNOHANG); |
|
503 |
else if (!client_attached) { |
||
504 |
if (sig == SIGTERM) |
||
505 |
proc_exit(client_proc); |
||
506 |
} else { |
||
507 |
switch (sig) { |
||
508 |
case SIGHUP: |
||
509 |
client_exitreason = CLIENT_EXIT_LOST_TTY; |
||
510 |
client_exitval = 1; |
||
511 |
proc_send(client_peer, MSG_EXITING, -1, NULL, 0); |
||
512 |
break; |
||
513 |
case SIGTERM: |
||
514 |
client_exitreason = CLIENT_EXIT_TERMINATED; |
||
515 |
client_exitval = 1; |
||
516 |
proc_send(client_peer, MSG_EXITING, -1, NULL, 0); |
||
517 |
break; |
||
518 |
case SIGWINCH: |
||
519 |
proc_send(client_peer, MSG_RESIZE, -1, NULL, 0); |
||
520 |
break; |
||
521 |
case SIGCONT: |
||
522 |
memset(&sigact, 0, sizeof sigact); |
||
523 |
sigemptyset(&sigact.sa_mask); |
||
524 |
sigact.sa_flags = SA_RESTART; |
||
525 |
sigact.sa_handler = SIG_IGN; |
||
526 |
if (sigaction(SIGTSTP, &sigact, NULL) != 0) |
||
527 |
fatal("sigaction failed"); |
||
528 |
proc_send(client_peer, MSG_WAKEUP, -1, NULL, 0); |
||
529 |
break; |
||
530 |
} |
||
531 |
} |
||
532 |
2 |
} |
|
533 |
|||
534 |
/* Callback for client read events. */ |
||
535 |
static void |
||
536 |
client_dispatch(struct imsg *imsg, __unused void *arg) |
||
537 |
{ |
||
538 |
✗✓ | 28 |
if (imsg == NULL) { |
539 |
client_exitreason = CLIENT_EXIT_LOST_SERVER; |
||
540 |
client_exitval = 1; |
||
541 |
proc_exit(client_proc); |
||
542 |
return; |
||
543 |
} |
||
544 |
|||
545 |
✓✓ | 14 |
if (client_attached) |
546 |
8 |
client_dispatch_attached(imsg); |
|
547 |
else |
||
548 |
6 |
client_dispatch_wait(imsg); |
|
549 |
14 |
} |
|
550 |
|||
551 |
/* Dispatch imsgs when in wait state (before MSG_READY). */ |
||
552 |
static void |
||
553 |
client_dispatch_wait(struct imsg *imsg) |
||
554 |
{ |
||
555 |
char *data; |
||
556 |
ssize_t datalen; |
||
557 |
12 |
struct msg_stdout_data stdoutdata; |
|
558 |
6 |
struct msg_stderr_data stderrdata; |
|
559 |
int retval; |
||
560 |
static int pledge_applied; |
||
561 |
|||
562 |
/* |
||
563 |
* "sendfd" is no longer required once all of the identify messages |
||
564 |
* have been sent. We know the server won't send us anything until that |
||
565 |
* point (because we don't ask it to), so we can drop "sendfd" once we |
||
566 |
* get the first message from the server. |
||
567 |
*/ |
||
568 |
✓✓ | 6 |
if (!pledge_applied) { |
569 |
✗✓ | 5 |
if (pledge("stdio unix proc exec tty flock rpath cpath wpath", NULL) != 0) |
570 |
fatal("pledge failed"); |
||
571 |
5 |
pledge_applied = 1; |
|
572 |
5 |
}; |
|
573 |
|||
574 |
12 |
data = imsg->data; |
|
575 |
12 |
datalen = imsg->hdr.len - IMSG_HEADER_SIZE; |
|
576 |
|||
577 |
✗✓✓✗ ✗✓✗✗ ✗✗✗✓ |
12 |
switch (imsg->hdr.type) { |
578 |
case MSG_EXIT: |
||
579 |
case MSG_SHUTDOWN: |
||
580 |
✗✓ | 1 |
if (datalen != sizeof retval && datalen != 0) |
581 |
fatalx("bad MSG_EXIT size"); |
||
582 |
✓✗ | 1 |
if (datalen == sizeof retval) { |
583 |
1 |
memcpy(&retval, data, sizeof retval); |
|
584 |
1 |
client_exitval = retval; |
|
585 |
1 |
} |
|
586 |
1 |
proc_exit(client_proc); |
|
587 |
1 |
break; |
|
588 |
case MSG_READY: |
||
589 |
✗✓ | 4 |
if (datalen != 0) |
590 |
fatalx("bad MSG_READY size"); |
||
591 |
|||
592 |
4 |
event_del(&client_stdin); |
|
593 |
4 |
client_attached = 1; |
|
594 |
4 |
proc_send(client_peer, MSG_RESIZE, -1, NULL, 0); |
|
595 |
4 |
break; |
|
596 |
case MSG_STDIN: |
||
597 |
if (datalen != 0) |
||
598 |
fatalx("bad MSG_STDIN size"); |
||
599 |
|||
600 |
event_add(&client_stdin, NULL); |
||
601 |
break; |
||
602 |
case MSG_STDOUT: |
||
603 |
if (datalen != sizeof stdoutdata) |
||
604 |
fatalx("bad MSG_STDOUT size"); |
||
605 |
memcpy(&stdoutdata, data, sizeof stdoutdata); |
||
606 |
|||
607 |
client_write(STDOUT_FILENO, stdoutdata.data, |
||
608 |
stdoutdata.size); |
||
609 |
break; |
||
610 |
case MSG_STDERR: |
||
611 |
✗✓ | 1 |
if (datalen != sizeof stderrdata) |
612 |
fatalx("bad MSG_STDERR size"); |
||
613 |
1 |
memcpy(&stderrdata, data, sizeof stderrdata); |
|
614 |
|||
615 |
2 |
client_write(STDERR_FILENO, stderrdata.data, |
|
616 |
1 |
stderrdata.size); |
|
617 |
1 |
break; |
|
618 |
case MSG_VERSION: |
||
619 |
if (datalen != 0) |
||
620 |
fatalx("bad MSG_VERSION size"); |
||
621 |
|||
622 |
fprintf(stderr, "protocol version mismatch " |
||
623 |
"(client %d, server %u)\n", PROTOCOL_VERSION, |
||
624 |
imsg->hdr.peerid & 0xff); |
||
625 |
client_exitval = 1; |
||
626 |
proc_exit(client_proc); |
||
627 |
break; |
||
628 |
case MSG_SHELL: |
||
629 |
if (datalen == 0 || data[datalen - 1] != '\0') |
||
630 |
fatalx("bad MSG_SHELL string"); |
||
631 |
|||
632 |
client_exec(data, shell_command); |
||
633 |
/* NOTREACHED */ |
||
634 |
case MSG_DETACH: |
||
635 |
case MSG_DETACHKILL: |
||
636 |
proc_send(client_peer, MSG_EXITING, -1, NULL, 0); |
||
637 |
break; |
||
638 |
case MSG_EXITED: |
||
639 |
proc_exit(client_proc); |
||
640 |
break; |
||
641 |
} |
||
642 |
6 |
} |
|
643 |
|||
644 |
/* Dispatch imsgs in attached state (after MSG_READY). */ |
||
645 |
static void |
||
646 |
client_dispatch_attached(struct imsg *imsg) |
||
647 |
{ |
||
648 |
24 |
struct sigaction sigact; |
|
649 |
char *data; |
||
650 |
ssize_t datalen; |
||
651 |
|||
652 |
16 |
data = imsg->data; |
|
653 |
16 |
datalen = imsg->hdr.len - IMSG_HEADER_SIZE; |
|
654 |
|||
655 |
✗✓✗✓ ✓✗✗✗ ✓ |
16 |
switch (imsg->hdr.type) { |
656 |
case MSG_DETACH: |
||
657 |
case MSG_DETACHKILL: |
||
658 |
✓✗✗✓ |
2 |
if (datalen == 0 || data[datalen - 1] != '\0') |
659 |
fatalx("bad MSG_DETACH string"); |
||
660 |
|||
661 |
1 |
client_exitsession = xstrdup(data); |
|
662 |
1 |
client_exittype = imsg->hdr.type; |
|
663 |
1 |
if (imsg->hdr.type == MSG_DETACHKILL) |
|
664 |
client_exitreason = CLIENT_EXIT_DETACHED_HUP; |
||
665 |
else |
||
666 |
client_exitreason = CLIENT_EXIT_DETACHED; |
||
667 |
1 |
proc_send(client_peer, MSG_EXITING, -1, NULL, 0); |
|
668 |
1 |
break; |
|
669 |
case MSG_EXEC: |
||
670 |
if (datalen == 0 || data[datalen - 1] != '\0' || |
||
671 |
strlen(data) + 1 == (size_t)datalen) |
||
672 |
fatalx("bad MSG_EXEC string"); |
||
673 |
client_execcmd = xstrdup(data); |
||
674 |
client_execshell = xstrdup(data + strlen(data) + 1); |
||
675 |
|||
676 |
client_exittype = imsg->hdr.type; |
||
677 |
proc_send(client_peer, MSG_EXITING, -1, NULL, 0); |
||
678 |
break; |
||
679 |
case MSG_EXIT: |
||
680 |
✗✓ | 3 |
if (datalen != 0 && datalen != sizeof (int)) |
681 |
fatalx("bad MSG_EXIT size"); |
||
682 |
|||
683 |
3 |
proc_send(client_peer, MSG_EXITING, -1, NULL, 0); |
|
684 |
3 |
client_exitreason = CLIENT_EXIT_EXITED; |
|
685 |
3 |
break; |
|
686 |
case MSG_EXITED: |
||
687 |
✗✓ | 4 |
if (datalen != 0) |
688 |
fatalx("bad MSG_EXITED size"); |
||
689 |
|||
690 |
4 |
proc_exit(client_proc); |
|
691 |
4 |
break; |
|
692 |
case MSG_SHUTDOWN: |
||
693 |
if (datalen != 0) |
||
694 |
fatalx("bad MSG_SHUTDOWN size"); |
||
695 |
|||
696 |
proc_send(client_peer, MSG_EXITING, -1, NULL, 0); |
||
697 |
client_exitreason = CLIENT_EXIT_SERVER_EXITED; |
||
698 |
client_exitval = 1; |
||
699 |
break; |
||
700 |
case MSG_SUSPEND: |
||
701 |
if (datalen != 0) |
||
702 |
fatalx("bad MSG_SUSPEND size"); |
||
703 |
|||
704 |
memset(&sigact, 0, sizeof sigact); |
||
705 |
sigemptyset(&sigact.sa_mask); |
||
706 |
sigact.sa_flags = SA_RESTART; |
||
707 |
sigact.sa_handler = SIG_DFL; |
||
708 |
if (sigaction(SIGTSTP, &sigact, NULL) != 0) |
||
709 |
fatal("sigaction failed"); |
||
710 |
kill(getpid(), SIGTSTP); |
||
711 |
break; |
||
712 |
case MSG_LOCK: |
||
713 |
if (datalen == 0 || data[datalen - 1] != '\0') |
||
714 |
fatalx("bad MSG_LOCK string"); |
||
715 |
|||
716 |
system(data); |
||
717 |
proc_send(client_peer, MSG_UNLOCK, -1, NULL, 0); |
||
718 |
break; |
||
719 |
} |
||
720 |
8 |
} |
Generated by: GCOVR (Version 3.3) |