comparison src/master/login-process.c @ 4538:9d9e72374164 HEAD

Fixes to login process handling, especially with login_process_per_connection=no. Removed login_max_logging_users setting since it was somewhat weird in how it worked. Added login_max_connections to replace it with login_process_per_connection=no, and with =yes its functionality is now within login_max_processes_count.
author Timo Sirainen <tss@iki.fi>
date Sun, 06 Aug 2006 23:05:32 +0300
parents af61031c746f
children 9dc62b7594de
comparison
equal deleted inserted replaced
4537:555c27e58cb1 4538:9d9e72374164
19 #include <unistd.h> 19 #include <unistd.h>
20 #include <syslog.h> 20 #include <syslog.h>
21 21
22 struct login_process { 22 struct login_process {
23 struct login_group *group; 23 struct login_group *group;
24 struct login_process *prev_nonlisten, *next_nonlisten; 24 struct login_process *prev_prelogin, *next_prelogin;
25 int refcount; 25 int refcount;
26 26
27 pid_t pid; 27 pid_t pid;
28 int fd; 28 int fd;
29 struct io *io; 29 struct io *io;
30 struct ostream *output; 30 struct ostream *output;
31 enum master_login_state state;
32
31 unsigned int initialized:1; 33 unsigned int initialized:1;
32 unsigned int listening:1;
33 unsigned int destroyed:1; 34 unsigned int destroyed:1;
35 unsigned int inetd_child:1;
34 }; 36 };
35 37
36 struct login_auth_request { 38 struct login_auth_request {
37 struct login_process *process; 39 struct login_process *process;
38 unsigned int tag; 40 unsigned int tag;
112 i_error("close(mail client) failed: %m"); 114 i_error("close(mail client) failed: %m");
113 login_process_unref(request->process); 115 login_process_unref(request->process);
114 i_free(request); 116 i_free(request);
115 } 117 }
116 118
117 static void login_process_mark_nonlistening(struct login_process *p) 119 static void process_remove_from_prelogin_lists(struct login_process *p)
118 { 120 {
119 if (!p->listening) { 121 if (p->state != LOGIN_STATE_FULL_PRELOGINS)
120 i_error("login: received another \"not listening\" " 122 return;
121 "notification (if you can't login at all, " 123
122 "see src/lib/fdpass.c)"); 124 if (p->prev_prelogin == NULL)
123 return; 125 p->group->oldest_prelogin_process = p->next_prelogin;
124 } 126 else
125 127 p->prev_prelogin->next_prelogin = p->next_prelogin;
126 p->listening = FALSE; 128
127 129 if (p->next_prelogin == NULL)
128 if (p->group != NULL) { 130 p->group->newest_prelogin_process = p->prev_prelogin;
131 else
132 p->next_prelogin->prev_prelogin = p->prev_prelogin;
133
134 p->prev_prelogin = p->next_prelogin = NULL;
135 }
136
137 static void process_mark_nonlistening(struct login_process *p,
138 enum master_login_state new_state)
139 {
140 if (p->group == NULL)
141 return;
142
143 if (p->state == LOGIN_STATE_LISTENING)
129 p->group->listening_processes--; 144 p->group->listening_processes--;
130 p->prev_nonlisten = p->group->newest_nonlisten_process; 145
131 146 if (new_state == LOGIN_STATE_FULL_PRELOGINS) {
132 if (p->group->newest_nonlisten_process != NULL) 147 /* add to prelogin list */
133 p->group->newest_nonlisten_process->next_nonlisten = p; 148 i_assert(p->state != new_state);
134 p->group->newest_nonlisten_process = p; 149
135 150 p->prev_prelogin = p->group->newest_prelogin_process;
136 if (p->group->oldest_nonlisten_process == NULL) 151 if (p->group->newest_prelogin_process == NULL)
137 p->group->oldest_nonlisten_process = p; 152 p->group->oldest_prelogin_process = p;
138 } 153 else
154 p->group->newest_prelogin_process->next_prelogin = p;
155 p->group->newest_prelogin_process = p;
156 } else {
157 process_remove_from_prelogin_lists(p);
158 }
159 }
160
161 static void process_mark_listening(struct login_process *p)
162 {
163 if (p->group == NULL)
164 return;
165
166 if (p->state != LOGIN_STATE_LISTENING)
167 p->group->listening_processes++;
168
169 process_remove_from_prelogin_lists(p);
170 }
171
172 static void
173 login_process_set_state(struct login_process *p, enum master_login_state state)
174 {
175 if (state == p->state || state > LOGIN_STATE_COUNT ||
176 (state < p->state && p->group->set->login_process_per_connection)) {
177 i_error("login: tried to change state %d -> %d "
178 "(if you can't login at all, see src/lib/fdpass.c)",
179 p->state, state);
180 return;
181 }
182
183 if (state == LOGIN_STATE_LISTENING) {
184 process_mark_listening(p);
185 } else {
186 process_mark_nonlistening(p, state);
187 }
188
189 p->state = state;
139 } 190 }
140 191
141 static void login_process_groups_create(void) 192 static void login_process_groups_create(void)
142 { 193 {
143 struct server_settings *server; 194 struct server_settings *server;
269 return; 320 return;
270 } 321 }
271 322
272 if (client_fd == -1) { 323 if (client_fd == -1) {
273 /* just a notification that the login process */ 324 /* just a notification that the login process */
325 enum master_login_state state = req.tag;
326
274 if (!p->initialized) { 327 if (!p->initialized) {
275 /* initialization notify */ 328 /* initialization notify */
276 p->initialized = TRUE;; 329 p->initialized = TRUE;;
277 } else { 330 } else {
278 /* not listening for new connections anymore */ 331 /* change "listening for new connections" status */
279 login_process_mark_nonlistening(p); 332 login_process_set_state(p, state);
280 } 333 }
281 return; 334 return;
282 } 335 }
283 336
284 fd_close_on_exec(client_fd, TRUE); 337 fd_close_on_exec(client_fd, TRUE);
314 p = i_new(struct login_process, 1); 367 p = i_new(struct login_process, 1);
315 p->group = group; 368 p->group = group;
316 p->refcount = 1; 369 p->refcount = 1;
317 p->pid = pid; 370 p->pid = pid;
318 p->fd = fd; 371 p->fd = fd;
319 p->listening = TRUE;
320 p->io = io_add(fd, IO_READ, login_process_input, p); 372 p->io = io_add(fd, IO_READ, login_process_input, p);
321 p->output = o_stream_create_file(fd, default_pool, 373 p->output = o_stream_create_file(fd, default_pool,
322 sizeof(struct master_login_reply)*10, 374 sizeof(struct master_login_reply)*10,
323 FALSE); 375 FALSE);
324 376
325 PID_ADD_PROCESS_TYPE(pid, PROCESS_TYPE_LOGIN); 377 PID_ADD_PROCESS_TYPE(pid, PROCESS_TYPE_LOGIN);
326 hash_insert(processes, POINTER_CAST(pid), p); 378 hash_insert(processes, POINTER_CAST(pid), p);
327 379
380 p->state = LOGIN_STATE_LISTENING;
381
328 if (p->group != NULL) { 382 if (p->group != NULL) {
329 p->group->processes++; 383 p->group->processes++;
330 p->group->listening_processes++; 384 p->group->listening_processes++;
331 } 385 }
332 return p; 386 return p;
333 } 387 }
334 388
335 static void login_process_remove_from_lists(struct login_process *p) 389 static void login_process_exited(struct login_process *p)
336 { 390 {
337 if (p->group == NULL) 391 if (p->group != NULL)
338 return; 392 p->group->processes--;
339 393
340 if (p == p->group->oldest_nonlisten_process) 394 hash_remove(processes, POINTER_CAST(p->pid));
341 p->group->oldest_nonlisten_process = p->next_nonlisten; 395 login_process_unref(p);
342 else
343 p->prev_nonlisten->next_nonlisten = p->next_nonlisten;
344
345 if (p == p->group->newest_nonlisten_process)
346 p->group->newest_nonlisten_process = p->prev_nonlisten;
347 else
348 p->next_nonlisten->prev_nonlisten = p->prev_nonlisten;
349
350 p->next_nonlisten = p->prev_nonlisten = NULL;
351 } 396 }
352 397
353 static void login_process_destroy(struct login_process *p) 398 static void login_process_destroy(struct login_process *p)
354 { 399 {
355 if (p->destroyed) 400 if (p->destroyed)
358 403
359 if (!p->initialized && io_loop_is_running(ioloop)) { 404 if (!p->initialized && io_loop_is_running(ioloop)) {
360 i_error("Login process died too early - shutting down"); 405 i_error("Login process died too early - shutting down");
361 io_loop_stop(ioloop); 406 io_loop_stop(ioloop);
362 } 407 }
363
364 if (p->listening && p->group != NULL)
365 p->group->listening_processes--;
366 408
367 o_stream_close(p->output); 409 o_stream_close(p->output);
368 io_remove(&p->io); 410 io_remove(&p->io);
369 if (close(p->fd) < 0) 411 if (close(p->fd) < 0)
370 i_error("close(login) failed: %m"); 412 i_error("close(login) failed: %m");
371 413
372 if (!p->listening) 414 process_mark_nonlistening(p, LOGIN_STATE_FULL_LOGINS);
373 login_process_remove_from_lists(p); 415
374 416 if (p->inetd_child)
375 if (p->group != NULL) 417 login_process_exited(p);
376 p->group->processes--;
377
378 if (p->pid != 0)
379 hash_remove(processes, POINTER_CAST(p->pid));
380
381 login_process_unref(p);
382 } 418 }
383 419
384 static void login_process_unref(struct login_process *p) 420 static void login_process_unref(struct login_process *p)
385 { 421 {
386 if (--p->refcount > 0) 422 if (--p->refcount > 0)
443 479
444 if (set->login_process_per_connection) { 480 if (set->login_process_per_connection) {
445 env_put("PROCESS_PER_CONNECTION=1"); 481 env_put("PROCESS_PER_CONNECTION=1");
446 env_put("MAX_LOGGING_USERS=1"); 482 env_put("MAX_LOGGING_USERS=1");
447 } else { 483 } else {
448 env_put(t_strdup_printf("MAX_LOGGING_USERS=%u", 484 env_put(t_strdup_printf("MAX_CONNECTIONS=%u",
449 set->login_max_logging_users)); 485 set->login_max_connections));
450 } 486 }
451 487
452 env_put(t_strconcat("PROCESS_UID=", dec2str(pid), NULL)); 488 env_put(t_strconcat("PROCESS_UID=", dec2str(pid), NULL));
453 env_put(t_strconcat("GREETING=", set->login_greeting, NULL)); 489 env_put(t_strconcat("GREETING=", set->login_greeting, NULL));
454 env_put(t_strconcat("LOG_FORMAT_ELEMENTS=", 490 env_put(t_strconcat("LOG_FORMAT_ELEMENTS=",
470 struct log_io *log; 506 struct log_io *log;
471 unsigned int max_log_lines_per_sec; 507 unsigned int max_log_lines_per_sec;
472 const char *prefix; 508 const char *prefix;
473 pid_t pid; 509 pid_t pid;
474 int fd[2], log_fd; 510 int fd[2], log_fd;
475
476 if (group->set->login_process_per_connection &&
477 group->processes - group->listening_processes >=
478 group->set->login_max_logging_users) {
479 if (group->oldest_nonlisten_process != NULL)
480 login_process_destroy(group->oldest_nonlisten_process);
481 }
482 511
483 if (group->set->login_uid == 0) 512 if (group->set->login_uid == 0)
484 i_fatal("Login process must not run as root"); 513 i_fatal("Login process must not run as root");
485 514
486 /* create communication to process with a socket pair */ 515 /* create communication to process with a socket pair */
567 i_fatal_status(FATAL_EXEC, "execv(%s) failed: %m", 596 i_fatal_status(FATAL_EXEC, "execv(%s) failed: %m",
568 group->set->login_executable); 597 group->set->login_executable);
569 return -1; 598 return -1;
570 } 599 }
571 600
572 void login_process_abormal_exit(pid_t pid) 601 void login_process_destroyed(pid_t pid, bool abnormal_exit)
573 { 602 {
574 struct login_process *p; 603 struct login_process *p;
575 604
576 /* don't start raising the process count if they're dying all
577 the time */
578 p = hash_lookup(processes, POINTER_CAST(pid)); 605 p = hash_lookup(processes, POINTER_CAST(pid));
579 if (p != NULL && p->group != NULL) 606 if (p == NULL)
580 p->group->wanted_processes_count = 0; 607 i_panic("Lost login process PID %s", dec2str(pid));
581 } 608 i_assert(!p->inetd_child);
582 609
583 void login_processes_destroy_all(void) 610 if (abnormal_exit) {
611 /* don't start raising the process count if they're dying all
612 the time */
613 if (p->group != NULL)
614 p->group->wanted_processes_count = 0;
615 }
616
617 login_process_destroy(p);
618 login_process_exited(p);
619 }
620
621 void login_processes_destroy_all(bool unref)
584 { 622 {
585 struct hash_iterate_context *iter; 623 struct hash_iterate_context *iter;
586 void *key, *value; 624 void *key, *value;
587 625
588 iter = hash_iterate_init(processes); 626 iter = hash_iterate_init(processes);
589 while (hash_iterate(iter, &key, &value)) 627 while (hash_iterate(iter, &key, &value)) {
590 login_process_destroy(value); 628 login_process_destroy(value);
629 if (unref) login_process_unref(value);
630 }
591 hash_iterate_deinit(iter); 631 hash_iterate_deinit(iter);
592 632
593 while (login_groups != NULL) { 633 while (login_groups != NULL) {
594 struct login_group *group = login_groups; 634 struct login_group *group = login_groups;
595 635
596 login_groups = group->next; 636 login_groups = group->next;
597 login_group_destroy(group); 637 login_group_destroy(group);
598 } 638 }
599 } 639 }
600 640
641 static void login_processes_notify_group(struct login_group *group)
642 {
643 struct hash_iterate_context *iter;
644 struct master_login_reply reply;
645 void *key, *value;
646
647 memset(&reply, 0, sizeof(reply));
648
649 iter = hash_iterate_init(processes);
650 while (hash_iterate(iter, &key, &value)) {
651 struct login_process *p = value;
652
653 if (p->group == group)
654 (void)o_stream_send(p->output, &reply, sizeof(reply));
655 }
656 hash_iterate_deinit(iter);
657 }
658
601 static int login_group_start_missings(struct login_group *group) 659 static int login_group_start_missings(struct login_group *group)
602 { 660 {
603 if (!group->set->login_process_per_connection) { 661 if (group->set->login_process_per_connection &&
604 /* create max. one process every second, that way if it keeps 662 group->processes >= group->set->login_max_processes_count &&
605 dying all the time we don't eat all cpu with fork()ing. */ 663 group->listening_processes == 0) {
606 if (group->listening_processes < 664 /* destroy the oldest listening process. non-listening
607 group->set->login_processes_count) { 665 processes are logged in users who we don't want to kick out
608 if (create_login_process(group) < 0) 666 because someone's started flooding */
609 return -1; 667 if (group->oldest_prelogin_process != NULL)
610 } 668 login_process_destroy(group->oldest_prelogin_process);
611 return 0;
612 } 669 }
613 670
614 /* we want to respond fast when multiple clients are connecting 671 /* we want to respond fast when multiple clients are connecting
615 at once, but we also want to prevent fork-bombing. use the 672 at once, but we also want to prevent fork-bombing. use the
616 same method as apache: check once a second if we need new 673 same method as apache: check once a second if we need new
625 group->wanted_processes_count *= 2; 682 group->wanted_processes_count *= 2;
626 else if (group->wanted_processes_count > 683 else if (group->wanted_processes_count >
627 group->set->login_processes_count) 684 group->set->login_processes_count)
628 group->wanted_processes_count--; 685 group->wanted_processes_count--;
629 686
630 if (group->wanted_processes_count > 687 while (group->listening_processes < group->wanted_processes_count &&
631 group->set->login_max_processes_count) { 688 group->processes < group->set->login_max_processes_count) {
632 group->wanted_processes_count =
633 group->set->login_max_processes_count;
634 }
635
636 while (group->listening_processes < group->wanted_processes_count) {
637 if (create_login_process(group) < 0) 689 if (create_login_process(group) < 0)
638 return -1; 690 return -1;
691 }
692
693 if (group->listening_processes == 0 &&
694 !group->set->login_process_per_connection) {
695 /* we've reached our limit. notify the processes to start
696 listening again which makes them kill some of their
697 oldest clients when accepting the next connection */
698 login_processes_notify_group(group);
639 } 699 }
640 return 0; 700 return 0;
641 } 701 }
642 702
643 static void login_processes_stall(void) 703 static void login_processes_stall(void)
742 net_set_nonblock(fd, TRUE); 802 net_set_nonblock(fd, TRUE);
743 fd_close_on_exec(fd, TRUE); 803 fd_close_on_exec(fd, TRUE);
744 804
745 p = login_process_new(NULL, ++login_pid_counter, fd); 805 p = login_process_new(NULL, ++login_pid_counter, fd);
746 p->initialized = TRUE; 806 p->initialized = TRUE;
807 p->inetd_child = TRUE;
747 } 808 }
748 } 809 }
749 810
750 void login_processes_init(void) 811 void login_processes_init(void)
751 { 812 {
769 if (to != NULL) 830 if (to != NULL)
770 timeout_remove(&to); 831 timeout_remove(&to);
771 if (io_listen != NULL) 832 if (io_listen != NULL)
772 io_remove(&io_listen); 833 io_remove(&io_listen);
773 834
774 login_processes_destroy_all(); 835 login_processes_destroy_all(TRUE);
775 hash_destroy(processes); 836 hash_destroy(processes);
776 } 837 }