Mercurial > dovecot > original-hg > dovecot-1.2
comparison src/master/login-process.c @ 1610:6850142c4e25 HEAD
New configuration file code. Some syntax changes, but tries to be somewhat
backwards compatible. SIGHUP now reverts back to old configuration if it
detected errors in new one.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Thu, 10 Jul 2003 06:04:07 +0300 |
parents | e7c627bacaaf |
children | b3526668de78 |
comparison
equal
deleted
inserted
replaced
1609:5c2ad8ec50db | 1610:6850142c4e25 |
---|---|
15 #include "master-login-interface.h" | 15 #include "master-login-interface.h" |
16 | 16 |
17 #include <unistd.h> | 17 #include <unistd.h> |
18 #include <syslog.h> | 18 #include <syslog.h> |
19 | 19 |
20 struct login_group { | |
21 struct login_group *next; | |
22 | |
23 struct login_settings *set; | |
24 | |
25 unsigned int processes; | |
26 unsigned int listening_processes; | |
27 unsigned int wanted_processes_count; | |
28 | |
29 struct login_process *oldest_nonlisten_process; | |
30 struct login_process *newest_nonlisten_process; | |
31 | |
32 const char *executable; | |
33 const char *module_dir; | |
34 unsigned int process_size; | |
35 int process_type; | |
36 int *listen_fd, *ssl_listen_fd; | |
37 }; | |
38 | |
39 struct login_process { | 20 struct login_process { |
40 struct login_group *group; | 21 struct login_group *group; |
41 struct login_process *prev_nonlisten, *next_nonlisten; | 22 struct login_process *prev_nonlisten, *next_nonlisten; |
42 int refcount; | 23 int refcount; |
43 | 24 |
69 | 50 |
70 static void login_process_destroy(struct login_process *p); | 51 static void login_process_destroy(struct login_process *p); |
71 static void login_process_unref(struct login_process *p); | 52 static void login_process_unref(struct login_process *p); |
72 static int login_process_init_group(struct login_process *p); | 53 static int login_process_init_group(struct login_process *p); |
73 | 54 |
74 static void login_group_create(struct login_settings *login_set) | 55 static void login_group_create(struct settings *set) |
75 { | 56 { |
76 struct login_group *group; | 57 struct login_group *group; |
77 | 58 |
78 if (strstr(set->protocols, login_set->name) == NULL) { | |
79 /* not enabled */ | |
80 return; | |
81 } | |
82 | |
83 group = i_new(struct login_group, 1); | 59 group = i_new(struct login_group, 1); |
84 group->set = login_set; | 60 group->set = set; |
85 | 61 group->process_type = set->protocol == MAIL_PROTOCOL_IMAP ? |
86 if (strcmp(login_set->name, "imap") == 0) { | 62 PROCESS_TYPE_IMAP : PROCESS_TYPE_POP3; |
87 group->executable = set->imap_executable; | |
88 group->process_size = set->imap_process_size; | |
89 group->process_type = PROCESS_TYPE_IMAP; | |
90 group->listen_fd = &mail_fd[FD_IMAP]; | |
91 group->ssl_listen_fd = &mail_fd[FD_IMAPS]; | |
92 group->module_dir = !set->imap_use_modules ? NULL : | |
93 set->imap_modules; | |
94 } else if (strcmp(login_set->name, "pop3") == 0) { | |
95 group->executable = set->pop3_executable; | |
96 group->process_size = set->pop3_process_size; | |
97 group->process_type = PROCESS_TYPE_POP3; | |
98 group->listen_fd = &mail_fd[FD_POP3]; | |
99 group->ssl_listen_fd = &mail_fd[FD_POP3S]; | |
100 group->module_dir = !set->pop3_use_modules ? NULL : | |
101 set->pop3_modules; | |
102 } else | |
103 i_panic("Unknown login group name '%s'", login_set->name); | |
104 | 63 |
105 group->next = login_groups; | 64 group->next = login_groups; |
106 login_groups = group; | 65 login_groups = group; |
107 } | 66 } |
108 | 67 |
121 master_reply.success = FALSE; | 80 master_reply.success = FALSE; |
122 else { | 81 else { |
123 struct login_group *group = request->process->group; | 82 struct login_group *group = request->process->group; |
124 | 83 |
125 master_reply.success = | 84 master_reply.success = |
126 create_mail_process(request->fd, &request->ip, | 85 create_mail_process(group, request->fd, &request->ip, |
127 group->executable, | |
128 group->module_dir, | |
129 group->process_size, | |
130 group->process_type, | |
131 reply, (const char *) data); | 86 reply, (const char *) data); |
132 } | 87 } |
133 | 88 |
134 /* reply to login */ | 89 /* reply to login */ |
135 master_reply.tag = request->login_tag; | 90 master_reply.tag = request->login_tag; |
166 if (p->group->oldest_nonlisten_process == NULL) | 121 if (p->group->oldest_nonlisten_process == NULL) |
167 p->group->oldest_nonlisten_process = p; | 122 p->group->oldest_nonlisten_process = p; |
168 } | 123 } |
169 } | 124 } |
170 | 125 |
171 static struct login_group *login_group_process_find(const char *name) | 126 static void login_process_groups_create(void) |
127 { | |
128 struct server_settings *server; | |
129 | |
130 for (server = settings_root; server != NULL; server = server->next) { | |
131 if (server->imap != NULL) | |
132 login_group_create(server->imap); | |
133 if (server->pop3 != NULL) | |
134 login_group_create(server->pop3); | |
135 } | |
136 } | |
137 | |
138 static struct login_group * | |
139 login_group_process_find(const char *name, enum mail_protocol protocol) | |
172 { | 140 { |
173 struct login_group *group; | 141 struct login_group *group; |
174 struct login_settings *login; | 142 |
175 | 143 if (login_groups == NULL) |
176 if (login_groups == NULL) { | 144 login_process_groups_create(); |
177 for (login = set->logins; login != NULL; login = login->next) | |
178 login_group_create(login); | |
179 } | |
180 | 145 |
181 for (group = login_groups; group != NULL; group = group->next) { | 146 for (group = login_groups; group != NULL; group = group->next) { |
182 if (strcmp(group->set->name, name) == 0) | 147 if (strcmp(group->set->server->name, name) == 0 && |
148 group->set->protocol == protocol) | |
183 return group; | 149 return group; |
184 } | 150 } |
185 | 151 |
186 return NULL; | 152 return NULL; |
187 } | 153 } |
188 | 154 |
189 static int login_process_read_group(struct login_process *p) | 155 static int login_process_read_group(struct login_process *p) |
190 { | 156 { |
191 struct login_group *group; | 157 struct login_group *group; |
192 const char *name; | 158 const char *name, *proto; |
193 char buf[256]; | 159 char buf[256]; |
160 enum mail_protocol protocol; | |
194 unsigned int len; | 161 unsigned int len; |
195 ssize_t ret; | 162 ssize_t ret; |
196 | 163 |
197 /* read length */ | 164 /* read length */ |
198 ret = read(p->fd, buf, 1); | 165 ret = read(p->fd, buf, 1); |
199 if (ret != 1) | 166 if (ret != 1) |
200 len = 0; | 167 len = 0; |
201 else { | 168 else { |
202 len = buf[0]; | 169 len = buf[0]; |
203 if (len >= sizeof(buf)) { | 170 if (len >= sizeof(buf)) { |
204 i_error("login: Process name length too large"); | 171 i_error("login: Server name length too large"); |
205 return FALSE; | 172 return FALSE; |
206 } | 173 } |
207 | 174 |
208 ret = read(p->fd, buf, len); | 175 ret = read(p->fd, buf, len); |
209 } | 176 } |
210 | 177 |
211 if (ret < 0) | 178 if (ret < 0) |
212 i_error("login: read() failed: %m"); | 179 i_error("login: read() failed: %m"); |
213 else if (len == 0 || (size_t)ret != len) | 180 else if (len == 0 || (size_t)ret != len) |
214 i_error("login: Process name wasn't sent"); | 181 i_error("login: Server name wasn't sent"); |
215 else { | 182 else { |
216 name = t_strndup(buf, len); | 183 name = t_strndup(buf, len); |
217 group = login_group_process_find(name); | 184 proto = strchr(buf, '/'); |
185 if (proto == NULL) { | |
186 i_error("login: Missing protocol from server name '%s'", | |
187 name); | |
188 return FALSE; | |
189 } | |
190 name = t_strdup_until(buf, proto++); | |
191 | |
192 if (strcmp(proto, "imap") == 0) | |
193 protocol = MAIL_PROTOCOL_IMAP; | |
194 else if (strcmp(proto, "pop3") == 0) | |
195 protocol = MAIL_PROTOCOL_IMAP; | |
196 else { | |
197 i_error("login: Unknown protocol '%s'", proto); | |
198 return FALSE; | |
199 } | |
200 | |
201 group = login_group_process_find(name, protocol); | |
218 if (group == NULL) { | 202 if (group == NULL) { |
219 i_error("login: Unknown process group '%s'", name); | 203 i_error("login: Unknown server name '%s'", name); |
220 return FALSE; | 204 return FALSE; |
221 } | 205 } |
222 | 206 |
223 p->group = group; | 207 p->group = group; |
224 return login_process_init_group(p); | 208 return login_process_init_group(p); |
382 i_free(p); | 366 i_free(p); |
383 } | 367 } |
384 | 368 |
385 static void login_process_init_env(struct login_group *group, pid_t pid) | 369 static void login_process_init_env(struct login_group *group, pid_t pid) |
386 { | 370 { |
387 child_process_init_env(); | 371 struct settings *set = group->set; |
372 | |
373 child_process_init_env(set); | |
388 | 374 |
389 /* setup access environment - needs to be done after | 375 /* setup access environment - needs to be done after |
390 clean_child_process() since it clears environment */ | 376 clean_child_process() since it clears environment */ |
391 restrict_access_set_env(group->set->user, | 377 restrict_access_set_env(set->login_user, set->login_uid, set->login_gid, |
392 group->set->uid, set->login_gid, | |
393 set->login_chroot ? set->login_dir : NULL, | 378 set->login_chroot ? set->login_dir : NULL, |
394 0, 0); | 379 0, 0); |
395 | 380 |
396 env_put("DOVECOT_MASTER=1"); | 381 env_put("DOVECOT_MASTER=1"); |
397 | 382 |
398 if (!set->ssl_disable) { | 383 if (!set->ssl_disable) { |
399 env_put(t_strconcat("SSL_CERT_FILE=", | 384 env_put(t_strconcat("SSL_CERT_FILE=", |
400 set->ssl_cert_file, NULL)); | 385 set->ssl_cert_file, NULL)); |
401 env_put(t_strconcat("SSL_KEY_FILE=", set->ssl_key_file, NULL)); | 386 env_put(t_strconcat("SSL_KEY_FILE=", |
387 set->ssl_key_file, NULL)); | |
402 env_put(t_strconcat("SSL_PARAM_FILE=", | 388 env_put(t_strconcat("SSL_PARAM_FILE=", |
403 set->ssl_parameters_file, NULL)); | 389 set->ssl_parameters_file, NULL)); |
404 } | 390 } |
405 | 391 |
406 if (set->disable_plaintext_auth) | 392 if (set->disable_plaintext_auth) |
408 if (set->verbose_proctitle) | 394 if (set->verbose_proctitle) |
409 env_put("VERBOSE_PROCTITLE=1"); | 395 env_put("VERBOSE_PROCTITLE=1"); |
410 if (set->verbose_ssl) | 396 if (set->verbose_ssl) |
411 env_put("VERBOSE_SSL=1"); | 397 env_put("VERBOSE_SSL=1"); |
412 | 398 |
413 if (group->set->process_per_connection) { | 399 if (set->login_process_per_connection) { |
414 env_put("PROCESS_PER_CONNECTION=1"); | 400 env_put("PROCESS_PER_CONNECTION=1"); |
415 env_put("MAX_LOGGING_USERS=1"); | 401 env_put("MAX_LOGGING_USERS=1"); |
416 } else { | 402 } else { |
417 env_put(t_strdup_printf("MAX_LOGGING_USERS=%u", | 403 env_put(t_strdup_printf("MAX_LOGGING_USERS=%u", |
418 group->set->max_logging_users)); | 404 set->login_max_logging_users)); |
419 } | 405 } |
420 | 406 |
421 env_put(t_strdup_printf("PROCESS_UID=%s", dec2str(pid))); | 407 env_put(t_strdup_printf("PROCESS_UID=%s", dec2str(pid))); |
422 } | 408 } |
423 | 409 |
425 { | 411 { |
426 static const char *argv[] = { NULL, NULL }; | 412 static const char *argv[] = { NULL, NULL }; |
427 pid_t pid; | 413 pid_t pid; |
428 int fd[2]; | 414 int fd[2]; |
429 | 415 |
430 if (group->set->process_per_connection && | 416 if (group->set->login_process_per_connection && |
431 group->processes - group->listening_processes >= | 417 group->processes - group->listening_processes >= |
432 group->set->max_logging_users) { | 418 group->set->login_max_logging_users) { |
433 if (group->oldest_nonlisten_process != NULL) | 419 if (group->oldest_nonlisten_process != NULL) |
434 login_process_destroy(group->oldest_nonlisten_process); | 420 login_process_destroy(group->oldest_nonlisten_process); |
435 } | 421 } |
436 | 422 |
437 if (group->set->uid == 0) | 423 if (group->set->login_uid == 0) |
438 i_fatal("Login process must not run as root"); | 424 i_fatal("Login process must not run as root"); |
439 | 425 |
440 /* create communication to process with a socket pair */ | 426 /* create communication to process with a socket pair */ |
441 if (socketpair(AF_UNIX, SOCK_STREAM, 0, fd) == -1) { | 427 if (socketpair(AF_UNIX, SOCK_STREAM, 0, fd) == -1) { |
442 i_error("socketpair() failed: %m"); | 428 i_error("socketpair() failed: %m"); |
459 (void)close(fd[1]); | 445 (void)close(fd[1]); |
460 return pid; | 446 return pid; |
461 } | 447 } |
462 | 448 |
463 /* move the listen handle */ | 449 /* move the listen handle */ |
464 if (dup2(*group->listen_fd, LOGIN_LISTEN_FD) < 0) | 450 if (dup2(group->set->listen_fd, LOGIN_LISTEN_FD) < 0) |
465 i_fatal("login: dup2(listen_fd) failed: %m"); | 451 i_fatal("login: dup2(listen_fd) failed: %m"); |
466 fd_close_on_exec(LOGIN_LISTEN_FD, FALSE); | 452 fd_close_on_exec(LOGIN_LISTEN_FD, FALSE); |
467 | 453 |
468 /* move the SSL listen handle */ | 454 /* move the SSL listen handle */ |
469 if (dup2(*group->ssl_listen_fd, LOGIN_SSL_LISTEN_FD) < 0) | 455 if (dup2(group->set->ssl_listen_fd, LOGIN_SSL_LISTEN_FD) < 0) |
470 i_fatal("login: dup2(ssl_listen_fd) failed: %m"); | 456 i_fatal("login: dup2(ssl_listen_fd) failed: %m"); |
471 fd_close_on_exec(LOGIN_SSL_LISTEN_FD, FALSE); | 457 fd_close_on_exec(LOGIN_SSL_LISTEN_FD, FALSE); |
472 | 458 |
473 /* move communication handle */ | 459 /* move communication handle */ |
474 if (dup2(fd[1], LOGIN_MASTER_SOCKET_FD) < 0) | 460 if (dup2(fd[1], LOGIN_MASTER_SOCKET_FD) < 0) |
478 (void)close(fd[0]); | 464 (void)close(fd[0]); |
479 (void)close(fd[1]); | 465 (void)close(fd[1]); |
480 | 466 |
481 login_process_init_env(group, getpid()); | 467 login_process_init_env(group, getpid()); |
482 | 468 |
483 if (!set->login_chroot) { | 469 if (!group->set->login_chroot) { |
484 /* no chrooting, but still change to the directory */ | 470 /* no chrooting, but still change to the directory */ |
485 if (chdir(set->login_dir) < 0) | 471 if (chdir(group->set->login_dir) < 0) { |
486 i_fatal("chdir(%s) failed: %m", set->login_dir); | 472 i_fatal("chdir(%s) failed: %m", |
487 } | 473 group->set->login_dir); |
488 | 474 } |
489 restrict_process_size(group->set->process_size, (unsigned int)-1); | 475 } |
476 | |
477 restrict_process_size(group->set->login_process_size, (unsigned int)-1); | |
490 | 478 |
491 /* make sure we don't leak syslog fd, but do it last so that | 479 /* make sure we don't leak syslog fd, but do it last so that |
492 any errors above will be logged */ | 480 any errors above will be logged */ |
493 closelog(); | 481 closelog(); |
494 | 482 |
495 /* hide the path, it's ugly */ | 483 /* hide the path, it's ugly */ |
496 argv[0] = strrchr(group->set->executable, '/'); | 484 argv[0] = strrchr(group->set->login_executable, '/'); |
497 if (argv[0] == NULL) argv[0] = group->set->executable; else argv[0]++; | 485 if (argv[0] == NULL) |
498 | 486 argv[0] = group->set->login_executable; |
499 execv(group->set->executable, (char **) argv); | 487 else |
488 argv[0]++; | |
489 | |
490 execv(group->set->login_executable, (char **) argv); | |
500 | 491 |
501 i_fatal_status(FATAL_EXEC, "execv(%s) failed: %m", | 492 i_fatal_status(FATAL_EXEC, "execv(%s) failed: %m", |
502 group->set->executable); | 493 group->set->login_executable); |
503 return -1; | 494 return -1; |
504 } | 495 } |
505 | 496 |
506 void login_process_abormal_exit(pid_t pid) | 497 void login_process_abormal_exit(pid_t pid) |
507 { | 498 { |
532 } | 523 } |
533 } | 524 } |
534 | 525 |
535 static void login_group_start_missings(struct login_group *group) | 526 static void login_group_start_missings(struct login_group *group) |
536 { | 527 { |
537 if (!group->set->process_per_connection) { | 528 if (!group->set->login_process_per_connection) { |
538 /* create max. one process every second, that way if it keeps | 529 /* create max. one process every second, that way if it keeps |
539 dying all the time we don't eat all cpu with fork()ing. */ | 530 dying all the time we don't eat all cpu with fork()ing. */ |
540 if (group->listening_processes < group->set->processes_count) | 531 if (group->listening_processes < |
532 group->set->login_processes_count) | |
541 (void)create_login_process(group); | 533 (void)create_login_process(group); |
542 return; | 534 return; |
543 } | 535 } |
544 | 536 |
545 /* we want to respond fast when multiple clients are connecting | 537 /* we want to respond fast when multiple clients are connecting |
547 same method as apache: check once a second if we need new | 539 same method as apache: check once a second if we need new |
548 processes. if yes and we've used all the existing processes, | 540 processes. if yes and we've used all the existing processes, |
549 double their amount (unless we've hit the high limit). | 541 double their amount (unless we've hit the high limit). |
550 Then for each second that didn't use all existing processes, | 542 Then for each second that didn't use all existing processes, |
551 drop the max. process count by one. */ | 543 drop the max. process count by one. */ |
552 if (group->wanted_processes_count < group->set->processes_count) | 544 if (group->wanted_processes_count < group->set->login_processes_count) { |
553 group->wanted_processes_count = group->set->processes_count; | 545 group->wanted_processes_count = |
554 else if (group->listening_processes == 0) | 546 group->set->login_processes_count; |
547 } else if (group->listening_processes == 0) | |
555 group->wanted_processes_count *= 2; | 548 group->wanted_processes_count *= 2; |
556 else if (group->wanted_processes_count > group->set->processes_count) | 549 else if (group->wanted_processes_count > |
550 group->set->login_processes_count) | |
557 group->wanted_processes_count--; | 551 group->wanted_processes_count--; |
558 | 552 |
559 if (group->wanted_processes_count > group->set->max_processes_count) | 553 if (group->wanted_processes_count > |
560 group->wanted_processes_count = group->set->max_processes_count; | 554 group->set->login_max_processes_count) { |
555 group->wanted_processes_count = | |
556 group->set->login_max_processes_count; | |
557 } | |
561 | 558 |
562 while (group->listening_processes < group->wanted_processes_count) | 559 while (group->listening_processes < group->wanted_processes_count) |
563 (void)create_login_process(group); | 560 (void)create_login_process(group); |
564 } | 561 } |
565 | 562 |
566 static void | 563 static void |
567 login_processes_start_missing(void *context __attr_unused__) | 564 login_processes_start_missing(void *context __attr_unused__) |
568 { | 565 { |
569 struct login_group *group; | 566 struct login_group *group; |
570 struct login_settings *login; | 567 |
571 | 568 if (login_groups == NULL) |
572 if (login_groups == NULL) { | 569 login_process_groups_create(); |
573 for (login = set->logins; login != NULL; login = login->next) | |
574 login_group_create(login); | |
575 } | |
576 | 570 |
577 for (group = login_groups; group != NULL; group = group->next) | 571 for (group = login_groups; group != NULL; group = group->next) |
578 login_group_start_missings(group); | 572 login_group_start_missings(group); |
579 } | 573 } |
580 | 574 |