File: | src/usr.bin/tmux/server.c |
Warning: | line 219, column 7 1st function call argument is an uninitialized value |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* $OpenBSD: server.c,v 1.198 2021/06/10 07:45:43 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/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(*__errno()).h> | |||
27 | #include <event.h> | |||
28 | #include <fcntl.h> | |||
29 | #include <paths.h> | |||
30 | #include <signal.h> | |||
31 | #include <stdio.h> | |||
32 | #include <stdlib.h> | |||
33 | #include <string.h> | |||
34 | #include <termios.h> | |||
35 | #include <time.h> | |||
36 | #include <unistd.h> | |||
37 | ||||
38 | #include "tmux.h" | |||
39 | ||||
40 | /* | |||
41 | * Main server functions. | |||
42 | */ | |||
43 | ||||
44 | struct clients clients; | |||
45 | ||||
46 | struct tmuxproc *server_proc; | |||
47 | static int server_fd = -1; | |||
48 | static uint64_t server_client_flags; | |||
49 | static int server_exit; | |||
50 | static struct event server_ev_accept; | |||
51 | static struct event server_ev_tidy; | |||
52 | ||||
53 | struct cmd_find_state marked_pane; | |||
54 | ||||
55 | static u_int message_next; | |||
56 | struct message_list message_log; | |||
57 | ||||
58 | static int server_loop(void); | |||
59 | static void server_send_exit(void); | |||
60 | static void server_accept(int, short, void *); | |||
61 | static void server_signal(int); | |||
62 | static void server_child_signal(void); | |||
63 | static void server_child_exited(pid_t, int); | |||
64 | static void server_child_stopped(pid_t, int); | |||
65 | ||||
66 | /* Set marked pane. */ | |||
67 | void | |||
68 | server_set_marked(struct session *s, struct winlink *wl, struct window_pane *wp) | |||
69 | { | |||
70 | cmd_find_clear_state(&marked_pane, 0); | |||
71 | marked_pane.s = s; | |||
72 | marked_pane.wl = wl; | |||
73 | marked_pane.w = wl->window; | |||
74 | marked_pane.wp = wp; | |||
75 | } | |||
76 | ||||
77 | /* Clear marked pane. */ | |||
78 | void | |||
79 | server_clear_marked(void) | |||
80 | { | |||
81 | cmd_find_clear_state(&marked_pane, 0); | |||
82 | } | |||
83 | ||||
84 | /* Is this the marked pane? */ | |||
85 | int | |||
86 | server_is_marked(struct session *s, struct winlink *wl, struct window_pane *wp) | |||
87 | { | |||
88 | if (s == NULL((void *)0) || wl == NULL((void *)0) || wp == NULL((void *)0)) | |||
89 | return (0); | |||
90 | if (marked_pane.s != s || marked_pane.wl != wl) | |||
91 | return (0); | |||
92 | if (marked_pane.wp != wp) | |||
93 | return (0); | |||
94 | return (server_check_marked()); | |||
95 | } | |||
96 | ||||
97 | /* Check if the marked pane is still valid. */ | |||
98 | int | |||
99 | server_check_marked(void) | |||
100 | { | |||
101 | return (cmd_find_valid_state(&marked_pane)); | |||
102 | } | |||
103 | ||||
104 | /* Create server socket. */ | |||
105 | static int | |||
106 | server_create_socket(int flags, char **cause) | |||
107 | { | |||
108 | struct sockaddr_un sa; | |||
109 | size_t size; | |||
110 | mode_t mask; | |||
111 | int fd, saved_errno; | |||
112 | ||||
113 | memset(&sa, 0, sizeof sa); | |||
114 | sa.sun_family = AF_UNIX1; | |||
115 | size = strlcpy(sa.sun_path, socket_path, sizeof sa.sun_path); | |||
116 | if (size >= sizeof sa.sun_path) { | |||
117 | errno(*__errno()) = ENAMETOOLONG63; | |||
118 | goto fail; | |||
119 | } | |||
120 | unlink(sa.sun_path); | |||
121 | ||||
122 | if ((fd = socket(AF_UNIX1, SOCK_STREAM1, 0)) == -1) | |||
123 | goto fail; | |||
124 | ||||
125 | if (flags & CLIENT_DEFAULTSOCKET0x8000000) | |||
126 | mask = umask(S_IXUSR0000100|S_IXGRP0000010|S_IRWXO0000007); | |||
127 | else | |||
128 | mask = umask(S_IXUSR0000100|S_IRWXG0000070|S_IRWXO0000007); | |||
129 | if (bind(fd, (struct sockaddr *)&sa, sizeof sa) == -1) { | |||
130 | saved_errno = errno(*__errno()); | |||
131 | close(fd); | |||
132 | errno(*__errno()) = saved_errno; | |||
133 | goto fail; | |||
134 | } | |||
135 | umask(mask); | |||
136 | ||||
137 | if (listen(fd, 128) == -1) { | |||
138 | saved_errno = errno(*__errno()); | |||
139 | close(fd); | |||
140 | errno(*__errno()) = saved_errno; | |||
141 | goto fail; | |||
142 | } | |||
143 | setblocking(fd, 0); | |||
144 | ||||
145 | return (fd); | |||
146 | ||||
147 | fail: | |||
148 | if (cause != NULL((void *)0)) { | |||
149 | xasprintf(cause, "error creating %s (%s)", socket_path, | |||
150 | strerror(errno(*__errno()))); | |||
151 | } | |||
152 | return (-1); | |||
153 | } | |||
154 | ||||
155 | /* Tidy up every hour. */ | |||
156 | static void | |||
157 | server_tidy_event(__unused__attribute__((__unused__)) int fd, __unused__attribute__((__unused__)) short events, __unused__attribute__((__unused__)) void *data) | |||
158 | { | |||
159 | struct timeval tv = { .tv_sec = 3600 }; | |||
160 | uint64_t t = get_timer(); | |||
161 | ||||
162 | format_tidy_jobs(); | |||
163 | ||||
164 | log_debug("%s: took %llu milliseconds", __func__, | |||
165 | (unsigned long long)(get_timer() - t)); | |||
166 | evtimer_add(&server_ev_tidy, &tv)event_add(&server_ev_tidy, &tv); | |||
167 | } | |||
168 | ||||
169 | /* Fork new server. */ | |||
170 | int | |||
171 | server_start(struct tmuxproc *client, int flags, struct event_base *base, | |||
172 | int lockfd, char *lockfile) | |||
173 | { | |||
174 | int fd; | |||
| ||||
175 | sigset_t set, oldset; | |||
176 | struct client *c = NULL((void *)0); | |||
177 | char *cause = NULL((void *)0); | |||
178 | struct timeval tv = { .tv_sec = 3600 }; | |||
179 | ||||
180 | sigfillset(&set); | |||
181 | sigprocmask(SIG_BLOCK1, &set, &oldset); | |||
182 | ||||
183 | if (~flags & CLIENT_NOFORK0x40000000) { | |||
184 | if (proc_fork_and_daemon(&fd) != 0) { | |||
185 | sigprocmask(SIG_SETMASK3, &oldset, NULL((void *)0)); | |||
186 | return (fd); | |||
187 | } | |||
188 | } | |||
189 | proc_clear_signals(client, 0); | |||
190 | server_client_flags = flags; | |||
191 | ||||
192 | if (event_reinit(base) != 0) | |||
193 | fatalx("event_reinit failed"); | |||
194 | server_proc = proc_start("server"); | |||
195 | ||||
196 | proc_set_signals(server_proc, server_signal); | |||
197 | sigprocmask(SIG_SETMASK3, &oldset, NULL((void *)0)); | |||
198 | ||||
199 | if (log_get_level() > 1) | |||
200 | tty_create_log(); | |||
201 | if (pledge("stdio rpath wpath cpath fattr unix getpw recvfd proc exec " | |||
202 | "tty ps", NULL((void *)0)) != 0) | |||
203 | fatal("pledge failed"); | |||
204 | ||||
205 | input_key_build(); | |||
206 | RB_INIT(&windows)do { (&windows)->rbh_root = ((void *)0); } while (0); | |||
207 | RB_INIT(&all_window_panes)do { (&all_window_panes)->rbh_root = ((void *)0); } while (0); | |||
208 | TAILQ_INIT(&clients)do { (&clients)->tqh_first = ((void *)0); (&clients )->tqh_last = &(&clients)->tqh_first; } while ( 0); | |||
209 | RB_INIT(&sessions)do { (&sessions)->rbh_root = ((void *)0); } while (0); | |||
210 | key_bindings_init(); | |||
211 | TAILQ_INIT(&message_log)do { (&message_log)->tqh_first = ((void *)0); (&message_log )->tqh_last = &(&message_log)->tqh_first; } while (0); | |||
212 | ||||
213 | gettimeofday(&start_time, NULL((void *)0)); | |||
214 | ||||
215 | server_fd = server_create_socket(flags, &cause); | |||
216 | if (server_fd != -1) | |||
217 | server_update_socket(); | |||
218 | if (~flags & CLIENT_NOFORK0x40000000) | |||
219 | c = server_client_create(fd); | |||
| ||||
220 | else | |||
221 | options_set_number(global_options, "exit-empty", 0); | |||
222 | ||||
223 | if (lockfd >= 0) { | |||
224 | unlink(lockfile); | |||
225 | free(lockfile); | |||
226 | close(lockfd); | |||
227 | } | |||
228 | ||||
229 | if (cause != NULL((void *)0)) { | |||
230 | if (c != NULL((void *)0)) { | |||
231 | cmdq_append(c, cmdq_get_error(cause)); | |||
232 | c->flags |= CLIENT_EXIT0x4; | |||
233 | } | |||
234 | free(cause); | |||
235 | } | |||
236 | ||||
237 | evtimer_set(&server_ev_tidy, server_tidy_event, NULL)event_set(&server_ev_tidy, -1, 0, server_tidy_event, ((void *)0)); | |||
238 | evtimer_add(&server_ev_tidy, &tv)event_add(&server_ev_tidy, &tv); | |||
239 | ||||
240 | server_add_accept(0); | |||
241 | proc_loop(server_proc, server_loop); | |||
242 | ||||
243 | job_kill_all(); | |||
244 | status_prompt_save_history(); | |||
245 | ||||
246 | exit(0); | |||
247 | } | |||
248 | ||||
249 | /* Server loop callback. */ | |||
250 | static int | |||
251 | server_loop(void) | |||
252 | { | |||
253 | struct client *c; | |||
254 | u_int items; | |||
255 | ||||
256 | do { | |||
257 | items = cmdq_next(NULL((void *)0)); | |||
258 | TAILQ_FOREACH(c, &clients, entry)for((c) = ((&clients)->tqh_first); (c) != ((void *)0); (c) = ((c)->entry.tqe_next)) { | |||
259 | if (c->flags & CLIENT_IDENTIFIED0x40000) | |||
260 | items += cmdq_next(c); | |||
261 | } | |||
262 | } while (items != 0); | |||
263 | ||||
264 | server_client_loop(); | |||
265 | ||||
266 | if (!options_get_number(global_options, "exit-empty") && !server_exit) | |||
267 | return (0); | |||
268 | ||||
269 | if (!options_get_number(global_options, "exit-unattached")) { | |||
270 | if (!RB_EMPTY(&sessions)((&sessions)->rbh_root == ((void *)0))) | |||
271 | return (0); | |||
272 | } | |||
273 | ||||
274 | TAILQ_FOREACH(c, &clients, entry)for((c) = ((&clients)->tqh_first); (c) != ((void *)0); (c) = ((c)->entry.tqe_next)) { | |||
275 | if (c->session != NULL((void *)0)) | |||
276 | return (0); | |||
277 | } | |||
278 | ||||
279 | /* | |||
280 | * No attached clients therefore want to exit - flush any waiting | |||
281 | * clients but don't actually exit until they've gone. | |||
282 | */ | |||
283 | cmd_wait_for_flush(); | |||
284 | if (!TAILQ_EMPTY(&clients)(((&clients)->tqh_first) == ((void *)0))) | |||
285 | return (0); | |||
286 | ||||
287 | if (job_still_running()) | |||
288 | return (0); | |||
289 | ||||
290 | return (1); | |||
291 | } | |||
292 | ||||
293 | /* Exit the server by killing all clients and windows. */ | |||
294 | static void | |||
295 | server_send_exit(void) | |||
296 | { | |||
297 | struct client *c, *c1; | |||
298 | struct session *s, *s1; | |||
299 | ||||
300 | cmd_wait_for_flush(); | |||
301 | ||||
302 | TAILQ_FOREACH_SAFE(c, &clients, entry, c1)for ((c) = ((&clients)->tqh_first); (c) != ((void *)0) && ((c1) = ((c)->entry.tqe_next), 1); (c) = (c1)) { | |||
303 | if (c->flags & CLIENT_SUSPENDED0x40) | |||
304 | server_client_lost(c); | |||
305 | else { | |||
306 | c->flags |= CLIENT_EXIT0x4; | |||
307 | c->exit_type = CLIENT_EXIT_SHUTDOWN; | |||
308 | } | |||
309 | c->session = NULL((void *)0); | |||
310 | } | |||
311 | ||||
312 | RB_FOREACH_SAFE(s, sessions, &sessions, s1)for ((s) = sessions_RB_MINMAX(&sessions, -1); ((s) != ((void *)0)) && ((s1) = sessions_RB_NEXT(s), 1); (s) = (s1) ) | |||
313 | session_destroy(s, 1, __func__); | |||
314 | } | |||
315 | ||||
316 | /* Update socket execute permissions based on whether sessions are attached. */ | |||
317 | void | |||
318 | server_update_socket(void) | |||
319 | { | |||
320 | struct session *s; | |||
321 | static int last = -1; | |||
322 | int n, mode; | |||
323 | struct stat sb; | |||
324 | ||||
325 | n = 0; | |||
326 | RB_FOREACH(s, sessions, &sessions)for ((s) = sessions_RB_MINMAX(&sessions, -1); (s) != ((void *)0); (s) = sessions_RB_NEXT(s)) { | |||
327 | if (s->attached != 0) { | |||
328 | n++; | |||
329 | break; | |||
330 | } | |||
331 | } | |||
332 | ||||
333 | if (n != last) { | |||
334 | last = n; | |||
335 | ||||
336 | if (stat(socket_path, &sb) != 0) | |||
337 | return; | |||
338 | mode = sb.st_mode & ACCESSPERMS(0000700|0000070|0000007); | |||
339 | if (n != 0) { | |||
340 | if (mode & S_IRUSR0000400) | |||
341 | mode |= S_IXUSR0000100; | |||
342 | if (mode & S_IRGRP0000040) | |||
343 | mode |= S_IXGRP0000010; | |||
344 | if (mode & S_IROTH0000004) | |||
345 | mode |= S_IXOTH0000001; | |||
346 | } else | |||
347 | mode &= ~(S_IXUSR0000100|S_IXGRP0000010|S_IXOTH0000001); | |||
348 | chmod(socket_path, mode); | |||
349 | } | |||
350 | } | |||
351 | ||||
352 | /* Callback for server socket. */ | |||
353 | static void | |||
354 | server_accept(int fd, short events, __unused__attribute__((__unused__)) void *data) | |||
355 | { | |||
356 | struct sockaddr_storage sa; | |||
357 | socklen_t slen = sizeof sa; | |||
358 | int newfd; | |||
359 | ||||
360 | server_add_accept(0); | |||
361 | if (!(events & EV_READ0x02)) | |||
362 | return; | |||
363 | ||||
364 | newfd = accept(fd, (struct sockaddr *) &sa, &slen); | |||
365 | if (newfd == -1) { | |||
366 | if (errno(*__errno()) == EAGAIN35 || errno(*__errno()) == EINTR4 || errno(*__errno()) == ECONNABORTED53) | |||
367 | return; | |||
368 | if (errno(*__errno()) == ENFILE23 || errno(*__errno()) == EMFILE24) { | |||
369 | /* Delete and don't try again for 1 second. */ | |||
370 | server_add_accept(1); | |||
371 | return; | |||
372 | } | |||
373 | fatal("accept failed"); | |||
374 | } | |||
375 | if (server_exit) { | |||
376 | close(newfd); | |||
377 | return; | |||
378 | } | |||
379 | server_client_create(newfd); | |||
380 | } | |||
381 | ||||
382 | /* | |||
383 | * Add accept event. If timeout is nonzero, add as a timeout instead of a read | |||
384 | * event - used to backoff when running out of file descriptors. | |||
385 | */ | |||
386 | void | |||
387 | server_add_accept(int timeout) | |||
388 | { | |||
389 | struct timeval tv = { timeout, 0 }; | |||
390 | ||||
391 | if (server_fd == -1) | |||
392 | return; | |||
393 | ||||
394 | if (event_initialized(&server_ev_accept)((&server_ev_accept)->ev_flags & 0x80)) | |||
395 | event_del(&server_ev_accept); | |||
396 | ||||
397 | if (timeout == 0) { | |||
398 | event_set(&server_ev_accept, server_fd, EV_READ0x02, server_accept, | |||
399 | NULL((void *)0)); | |||
400 | event_add(&server_ev_accept, NULL((void *)0)); | |||
401 | } else { | |||
402 | event_set(&server_ev_accept, server_fd, EV_TIMEOUT0x01, | |||
403 | server_accept, NULL((void *)0)); | |||
404 | event_add(&server_ev_accept, &tv); | |||
405 | } | |||
406 | } | |||
407 | ||||
408 | /* Signal handler. */ | |||
409 | static void | |||
410 | server_signal(int sig) | |||
411 | { | |||
412 | int fd; | |||
413 | ||||
414 | log_debug("%s: %s", __func__, strsignal(sig)); | |||
415 | switch (sig) { | |||
416 | case SIGINT2: | |||
417 | case SIGTERM15: | |||
418 | server_exit = 1; | |||
419 | server_send_exit(); | |||
420 | break; | |||
421 | case SIGCHLD20: | |||
422 | server_child_signal(); | |||
423 | break; | |||
424 | case SIGUSR130: | |||
425 | event_del(&server_ev_accept); | |||
426 | fd = server_create_socket(server_client_flags, NULL((void *)0)); | |||
427 | if (fd != -1) { | |||
428 | close(server_fd); | |||
429 | server_fd = fd; | |||
430 | server_update_socket(); | |||
431 | } | |||
432 | server_add_accept(0); | |||
433 | break; | |||
434 | case SIGUSR231: | |||
435 | proc_toggle_log(server_proc); | |||
436 | break; | |||
437 | } | |||
438 | } | |||
439 | ||||
440 | /* Handle SIGCHLD. */ | |||
441 | static void | |||
442 | server_child_signal(void) | |||
443 | { | |||
444 | int status; | |||
445 | pid_t pid; | |||
446 | ||||
447 | for (;;) { | |||
448 | switch (pid = waitpid(WAIT_ANY(-1), &status, WNOHANG1|WUNTRACED2)) { | |||
449 | case -1: | |||
450 | if (errno(*__errno()) == ECHILD10) | |||
451 | return; | |||
452 | fatal("waitpid failed"); | |||
453 | case 0: | |||
454 | return; | |||
455 | } | |||
456 | if (WIFSTOPPED(status)(((status) & 0xff) == 0177)) | |||
457 | server_child_stopped(pid, status); | |||
458 | else if (WIFEXITED(status)(((status) & 0177) == 0) || WIFSIGNALED(status)(((status) & 0177) != 0177 && ((status) & 0177 ) != 0)) | |||
459 | server_child_exited(pid, status); | |||
460 | } | |||
461 | } | |||
462 | ||||
463 | /* Handle exited children. */ | |||
464 | static void | |||
465 | server_child_exited(pid_t pid, int status) | |||
466 | { | |||
467 | struct window *w, *w1; | |||
468 | struct window_pane *wp; | |||
469 | ||||
470 | RB_FOREACH_SAFE(w, windows, &windows, w1)for ((w) = windows_RB_MINMAX(&windows, -1); ((w) != ((void *)0)) && ((w1) = windows_RB_NEXT(w), 1); (w) = (w1)) { | |||
471 | TAILQ_FOREACH(wp, &w->panes, entry)for((wp) = ((&w->panes)->tqh_first); (wp) != ((void *)0); (wp) = ((wp)->entry.tqe_next)) { | |||
472 | if (wp->pid == pid) { | |||
473 | wp->status = status; | |||
474 | wp->flags |= PANE_STATUSREADY0x200; | |||
475 | ||||
476 | log_debug("%%%u exited", wp->id); | |||
477 | wp->flags |= PANE_EXITED0x100; | |||
478 | ||||
479 | if (window_pane_destroy_ready(wp)) | |||
480 | server_destroy_pane(wp, 1); | |||
481 | break; | |||
482 | } | |||
483 | } | |||
484 | } | |||
485 | job_check_died(pid, status); | |||
486 | } | |||
487 | ||||
488 | /* Handle stopped children. */ | |||
489 | static void | |||
490 | server_child_stopped(pid_t pid, int status) | |||
491 | { | |||
492 | struct window *w; | |||
493 | struct window_pane *wp; | |||
494 | ||||
495 | if (WSTOPSIG(status)(int)(((unsigned)(status) >> 8) & 0xff) == SIGTTIN21 || WSTOPSIG(status)(int)(((unsigned)(status) >> 8) & 0xff) == SIGTTOU22) | |||
496 | return; | |||
497 | ||||
498 | RB_FOREACH(w, windows, &windows)for ((w) = windows_RB_MINMAX(&windows, -1); (w) != ((void *)0); (w) = windows_RB_NEXT(w)) { | |||
499 | TAILQ_FOREACH(wp, &w->panes, entry)for((wp) = ((&w->panes)->tqh_first); (wp) != ((void *)0); (wp) = ((wp)->entry.tqe_next)) { | |||
500 | if (wp->pid == pid) { | |||
501 | if (killpg(pid, SIGCONT19) != 0) | |||
502 | kill(pid, SIGCONT19); | |||
503 | } | |||
504 | } | |||
505 | } | |||
506 | job_check_died(pid, status); | |||
507 | } | |||
508 | ||||
509 | /* Add to message log. */ | |||
510 | void | |||
511 | server_add_message(const char *fmt, ...) | |||
512 | { | |||
513 | struct message_entry *msg, *msg1; | |||
514 | char *s; | |||
515 | va_list ap; | |||
516 | u_int limit; | |||
517 | ||||
518 | va_start(ap, fmt)__builtin_va_start(ap, fmt); | |||
519 | xvasprintf(&s, fmt, ap); | |||
520 | va_end(ap)__builtin_va_end(ap); | |||
521 | ||||
522 | log_debug("message: %s", s); | |||
523 | ||||
524 | msg = xcalloc(1, sizeof *msg); | |||
525 | gettimeofday(&msg->msg_time, NULL((void *)0)); | |||
526 | msg->msg_num = message_next++; | |||
527 | msg->msg = s; | |||
528 | TAILQ_INSERT_TAIL(&message_log, msg, entry)do { (msg)->entry.tqe_next = ((void *)0); (msg)->entry. tqe_prev = (&message_log)->tqh_last; *(&message_log )->tqh_last = (msg); (&message_log)->tqh_last = & (msg)->entry.tqe_next; } while (0); | |||
529 | ||||
530 | limit = options_get_number(global_options, "message-limit"); | |||
531 | TAILQ_FOREACH_SAFE(msg, &message_log, entry, msg1)for ((msg) = ((&message_log)->tqh_first); (msg) != ((void *)0) && ((msg1) = ((msg)->entry.tqe_next), 1); (msg ) = (msg1)) { | |||
532 | if (msg->msg_num + limit >= message_next) | |||
533 | break; | |||
534 | free(msg->msg); | |||
535 | TAILQ_REMOVE(&message_log, msg, entry)do { if (((msg)->entry.tqe_next) != ((void *)0)) (msg)-> entry.tqe_next->entry.tqe_prev = (msg)->entry.tqe_prev; else (&message_log)->tqh_last = (msg)->entry.tqe_prev ; *(msg)->entry.tqe_prev = (msg)->entry.tqe_next; ; ; } while (0); | |||
536 | free(msg); | |||
537 | } | |||
538 | } |