main.c revision faec0abfd648c647030027e86de2ce8911df683b
a8c5a86d183db25a57bf193c06b41e092ec2e151Timo Sirainen/* Copyright (c) 2005-2011 Dovecot authors, see the included COPYING file */
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen#include "common.h"
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen#include "ioloop.h"
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen#include "lib-signals.h"
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen#include "fd-close-on-exec.h"
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen#include "array.h"
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen#include "write-full.h"
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen#include "env-util.h"
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen#include "hostpid.h"
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen#include "abspath.h"
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen#include "ipwd.h"
300e4e43ed1ca46d0614459161ca2fb460ef661aTimo Sirainen#include "execv-const.h"
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen#include "restrict-process-size.h"
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen#include "master-service.h"
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen#include "master-service-settings.h"
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen#include "askpass.h"
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen#include "capabilities.h"
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen#include "service.h"
ae46f6ba5bb9eee8900254d3042e89d490023be0Timo Sirainen#include "service-anvil.h"
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen#include "service-listen.h"
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen#include "service-monitor.h"
3f3ad16ff74d694796d22501250a9a29997c0729Timo Sirainen#include "service-process.h"
b4ddb5b3c3722620a8fef387dd8c47bb411a5643Timo Sirainen#include "service-log.h"
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen#include "dovecot-version.h"
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen
300e4e43ed1ca46d0614459161ca2fb460ef661aTimo Sirainen#include <stdio.h>
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen#include <stdlib.h>
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen#include <unistd.h>
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen#include <fcntl.h>
e4194f4703eeec32b432371ae30fc8f25ab720d8Timo Sirainen#include <sys/stat.h>
e4194f4703eeec32b432371ae30fc8f25ab720d8Timo Sirainen
e4194f4703eeec32b432371ae30fc8f25ab720d8Timo Sirainen#define DOVECOT_CONFIG_BIN_PATH BINDIR"/doveconf"
e4194f4703eeec32b432371ae30fc8f25ab720d8Timo Sirainen
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen#define MASTER_SERVICE_NAME "master"
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen#define FATAL_FILENAME "master-fatal.lastlog"
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen#define MASTER_PID_FILE_NAME "master.pid"
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen#define SERVICE_TIME_MOVED_BACKWARDS_MAX_THROTTLE_SECS (60*3)
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen
e34d170f8f0e084bd94bfbc1a7085ece67e508dfTimo Sirainenuid_t master_uid;
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainengid_t master_gid;
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainenbool core_dumps_disabled;
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainenconst char *ssl_manual_key_password;
db693bf6fcae96d834567f1782257517b7207655Timo Sirainenint null_fd, global_master_dead_pipe_fd[2];
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainenstruct service_list *services;
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen
faec0abfd648c647030027e86de2ce8911df683bTimo Sirainenstatic char *pidfile_path;
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainenstatic failure_callback_t *orig_fatal_callback;
5801ce4da7d807ab85d02051ece5969e7175eebaTimo Sirainenstatic failure_callback_t *orig_error_callback;
b4ddb5b3c3722620a8fef387dd8c47bb411a5643Timo Sirainen
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainenstatic const struct setting_parser_info *set_roots[] = {
300e4e43ed1ca46d0614459161ca2fb460ef661aTimo Sirainen &master_setting_parser_info,
300e4e43ed1ca46d0614459161ca2fb460ef661aTimo Sirainen NULL
300e4e43ed1ca46d0614459161ca2fb460ef661aTimo Sirainen};
300e4e43ed1ca46d0614459161ca2fb460ef661aTimo Sirainen
300e4e43ed1ca46d0614459161ca2fb460ef661aTimo Sirainenvoid process_exec(const char *cmd, const char *extra_args[])
db693bf6fcae96d834567f1782257517b7207655Timo Sirainen{
300e4e43ed1ca46d0614459161ca2fb460ef661aTimo Sirainen const char *executable, *p, **argv;
300e4e43ed1ca46d0614459161ca2fb460ef661aTimo Sirainen
300e4e43ed1ca46d0614459161ca2fb460ef661aTimo Sirainen argv = t_strsplit(cmd, " ");
300e4e43ed1ca46d0614459161ca2fb460ef661aTimo Sirainen executable = argv[0];
5801ce4da7d807ab85d02051ece5969e7175eebaTimo Sirainen
5801ce4da7d807ab85d02051ece5969e7175eebaTimo Sirainen if (extra_args != NULL) {
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen unsigned int count1, count2;
861f53be0cc2fa5665f3c107a7576e2a53bb2eb0Timo Sirainen const char **new_argv;
5801ce4da7d807ab85d02051ece5969e7175eebaTimo Sirainen
db693bf6fcae96d834567f1782257517b7207655Timo Sirainen /* @UNSAFE */
861f53be0cc2fa5665f3c107a7576e2a53bb2eb0Timo Sirainen count1 = str_array_length(argv);
861f53be0cc2fa5665f3c107a7576e2a53bb2eb0Timo Sirainen count2 = str_array_length(extra_args);
861f53be0cc2fa5665f3c107a7576e2a53bb2eb0Timo Sirainen new_argv = t_new(const char *, count1 + count2 + 1);
861f53be0cc2fa5665f3c107a7576e2a53bb2eb0Timo Sirainen memcpy(new_argv, argv, sizeof(const char *) * count1);
861f53be0cc2fa5665f3c107a7576e2a53bb2eb0Timo Sirainen memcpy(new_argv + count1, extra_args,
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen sizeof(const char *) * count2);
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen argv = new_argv;
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen }
faec0abfd648c647030027e86de2ce8911df683bTimo Sirainen
faec0abfd648c647030027e86de2ce8911df683bTimo Sirainen /* hide the path, it's ugly */
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen p = strrchr(argv[0], '/');
db693bf6fcae96d834567f1782257517b7207655Timo Sirainen if (p != NULL) argv[0] = p+1;
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen
b4ddb5b3c3722620a8fef387dd8c47bb411a5643Timo Sirainen /* prefix with dovecot/ */
b4ddb5b3c3722620a8fef387dd8c47bb411a5643Timo Sirainen argv[0] = t_strconcat(PACKAGE"/", argv[0], NULL);
b4ddb5b3c3722620a8fef387dd8c47bb411a5643Timo Sirainen (void)execv_const(executable, argv);
b4ddb5b3c3722620a8fef387dd8c47bb411a5643Timo Sirainen}
b4ddb5b3c3722620a8fef387dd8c47bb411a5643Timo Sirainen
b4ddb5b3c3722620a8fef387dd8c47bb411a5643Timo Sirainenint get_uidgid(const char *user, uid_t *uid_r, gid_t *gid_r,
b4ddb5b3c3722620a8fef387dd8c47bb411a5643Timo Sirainen const char **error_r)
faec0abfd648c647030027e86de2ce8911df683bTimo Sirainen{
fb35b9f2c80954da842c20d5128b5e506835d93eTimo Sirainen struct passwd pw;
b4ddb5b3c3722620a8fef387dd8c47bb411a5643Timo Sirainen
b4ddb5b3c3722620a8fef387dd8c47bb411a5643Timo Sirainen if (*user == '\0') {
b4ddb5b3c3722620a8fef387dd8c47bb411a5643Timo Sirainen *uid_r = (uid_t)-1;
e34d170f8f0e084bd94bfbc1a7085ece67e508dfTimo Sirainen *gid_r = (gid_t)-1;
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen return 0;
b4ddb5b3c3722620a8fef387dd8c47bb411a5643Timo Sirainen }
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen switch (i_getpwnam(user, &pw)) {
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen case -1:
fa87f0ef3636fa52ce42513533ee500c0bf29910Timo Sirainen *error_r = t_strdup_printf("getpwnam(%s) failed: %m", user);
fa87f0ef3636fa52ce42513533ee500c0bf29910Timo Sirainen return -1;
fa87f0ef3636fa52ce42513533ee500c0bf29910Timo Sirainen case 0:
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen *error_r = t_strdup_printf("User doesn't exist: %s", user);
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen return -1;
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen default:
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen *uid_r = pw.pw_uid;
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen *gid_r = pw.pw_gid;
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen return 0;
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen }
fa87f0ef3636fa52ce42513533ee500c0bf29910Timo Sirainen}
fa87f0ef3636fa52ce42513533ee500c0bf29910Timo Sirainen
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainenint get_gid(const char *group, gid_t *gid_r, const char **error_r)
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen{
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen struct group gr;
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen if (*group == '\0') {
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen *gid_r = (gid_t)-1;
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen return 0;
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen }
4ee00532a265bdfb38539d811fcd12d51210ac35Timo Sirainen
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen switch (i_getgrnam(group, &gr)) {
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen case -1:
242abe3ad2423776e9cf05e1304eb8fda4831b23Timo Sirainen *error_r = t_strdup_printf("getgrnam(%s) failed: %m", group);
cf9d67e4a9bfee31cf3be05244555d51a3d1b9feTimo Sirainen return -1;
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen case 0:
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen *error_r = t_strdup_printf("Group doesn't exist: %s", group);
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen return -1;
2efe19d9045768d985a3bd549cff12f65ba40cc8Timo Sirainen default:
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen *gid_r = gr.gr_gid;
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen return 0;
2efe19d9045768d985a3bd549cff12f65ba40cc8Timo Sirainen }
b6b9c99fefbbc662bd9a0006566133c4480bf0e8Timo Sirainen}
b6b9c99fefbbc662bd9a0006566133c4480bf0e8Timo Sirainen
2efe19d9045768d985a3bd549cff12f65ba40cc8Timo Sirainenstatic void ATTR_NORETURN ATTR_FORMAT(2, 0)
2efe19d9045768d985a3bd549cff12f65ba40cc8Timo Sirainenmaster_fatal_callback(const struct failure_context *ctx,
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen const char *format, va_list args)
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen{
2efe19d9045768d985a3bd549cff12f65ba40cc8Timo Sirainen const char *path, *str;
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen va_list args2;
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen int fd;
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen /* if we already forked a child process, this isn't fatal for the
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen main process and there's no need to write the fatal file. */
7ee226c2a66aa4dce7f13e8b17687db285c981bdTimo Sirainen if (getpid() == strtol(my_pid, NULL, 10)) {
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen /* write the error message to a file (we're chdired to
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen base dir) */
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen path = t_strconcat(FATAL_FILENAME, NULL);
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen fd = open(path, O_CREAT | O_TRUNC | O_WRONLY, 0600);
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen if (fd != -1) {
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen VA_COPY(args2, args);
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen str = t_strdup_vprintf(format, args2);
fa87f0ef3636fa52ce42513533ee500c0bf29910Timo Sirainen write_full(fd, str, strlen(str));
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen (void)close(fd);
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen }
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen }
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen orig_fatal_callback(ctx, format, args);
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen abort(); /* just to silence the noreturn attribute warnings */
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen}
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen
c4900d31385344bfadaee53a897daeafdb3063d8Timo Sirainenstatic void ATTR_NORETURN
cf9d67e4a9bfee31cf3be05244555d51a3d1b9feTimo Sirainenstartup_fatal_handler(const struct failure_context *ctx,
cf9d67e4a9bfee31cf3be05244555d51a3d1b9feTimo Sirainen const char *fmt, va_list args)
cf9d67e4a9bfee31cf3be05244555d51a3d1b9feTimo Sirainen{
edd318d5866ac3fbc6e8df28fb24a4dfef93c884Timo Sirainen va_list args2;
69b22a0c0c84087e5bdeec71faae7ea77295240fTimo Sirainen
69b22a0c0c84087e5bdeec71faae7ea77295240fTimo Sirainen VA_COPY(args2, args);
69b22a0c0c84087e5bdeec71faae7ea77295240fTimo Sirainen fprintf(stderr, "%s%s\n", failure_log_type_prefixes[ctx->type],
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen t_strdup_vprintf(fmt, args2));
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen orig_fatal_callback(ctx, fmt, args);
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen abort();
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen}
8dd76854cc680053986142d5f5e823f637447929Timo Sirainen
8dd76854cc680053986142d5f5e823f637447929Timo Sirainenstatic void
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainenstartup_error_handler(const struct failure_context *ctx,
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen const char *fmt, va_list args)
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen{
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen va_list args2;
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen VA_COPY(args2, args);
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen fprintf(stderr, "%s%s\n", failure_log_type_prefixes[ctx->type],
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen t_strdup_vprintf(fmt, args2));
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen orig_error_callback(ctx, fmt, args);
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen}
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainenstatic void fatal_log_check(const struct master_settings *set)
412b772c337428b72149605c1410524c2353e5d4Timo Sirainen{
412b772c337428b72149605c1410524c2353e5d4Timo Sirainen const char *path;
412b772c337428b72149605c1410524c2353e5d4Timo Sirainen char buf[1024];
0a53eb0283d7ec28c6105f61e118b96fce8ecb95Timo Sirainen ssize_t ret;
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen int fd;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen path = t_strconcat(set->base_dir, "/"FATAL_FILENAME, NULL);
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen fd = open(path, O_RDONLY);
412b772c337428b72149605c1410524c2353e5d4Timo Sirainen if (fd == -1)
f9f77e06a148fd0816004e0e1b0f585307148a7dTimo Sirainen return;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen ret = read(fd, buf, sizeof(buf));
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen if (ret < 0)
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen i_error("read(%s) failed: %m", path);
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen else {
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen buf[ret] = '\0';
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen fprintf(stderr, "Last died with error (see error log for more "
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen "information): %s\n", buf);
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen }
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen close(fd);
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen if (unlink(path) < 0)
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen i_error("unlink(%s) failed: %m", path);
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen}
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainenstatic bool pid_file_read(const char *path, pid_t *pid_r)
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen{
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen char buf[32];
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen int fd;
7ee226c2a66aa4dce7f13e8b17687db285c981bdTimo Sirainen ssize_t ret;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen bool found;
1df39b899804fd1dbc560f75382364822935c857Timo Sirainen
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen fd = open(path, O_RDONLY);
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen if (fd == -1) {
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen if (errno == ENOENT)
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen return FALSE;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen i_fatal("open(%s) failed: %m", path);
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen }
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen ret = read(fd, buf, sizeof(buf));
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen if (ret <= 0) {
d6b3cfd855c0eebed68be50d3111de1b5a6afeb0Timo Sirainen if (ret == 0)
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen i_error("Empty PID file in %s, overriding", path);
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen else
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen i_fatal("read(%s) failed: %m", path);
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen found = FALSE;
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen } else {
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen if (buf[ret-1] == '\n')
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen ret--;
buf[ret] = '\0';
*pid_r = atoi(buf);
found = !(*pid_r == getpid() ||
(kill(*pid_r, 0) < 0 && errno == ESRCH));
}
(void)close(fd);
return found;
}
static void pid_file_check_running(const char *path)
{
pid_t pid;
if (!pid_file_read(path, &pid))
return;
i_fatal("Dovecot is already running with PID %s "
"(read from %s)", dec2str(pid), path);
}
static void create_pid_file(const char *path)
{
const char *pid;
int fd;
pid = t_strconcat(dec2str(getpid()), "\n", NULL);
fd = open(path, O_WRONLY|O_CREAT|O_TRUNC, 0644);
if (fd == -1)
i_fatal("open(%s) failed: %m", path);
if (write_full(fd, pid, strlen(pid)) < 0)
i_fatal("write() failed in %s: %m", path);
(void)close(fd);
}
static void create_config_symlink(const struct master_settings *set)
{
const char *base_config_path;
base_config_path = t_strconcat(set->base_dir, "/"PACKAGE".conf", NULL);
if (unlink(base_config_path) < 0 && errno != ENOENT)
i_error("unlink(%s) failed: %m", base_config_path);
if (symlink(services->config->config_file_path, base_config_path) < 0) {
i_error("symlink(%s, %s) failed: %m",
services->config->config_file_path, base_config_path);
}
}
static void
sig_settings_reload(const siginfo_t *si ATTR_UNUSED,
void *context ATTR_UNUSED)
{
struct master_service_settings_input input;
struct master_service_settings_output output;
const struct master_settings *set;
void **sets;
struct service_list *new_services;
struct service *service;
const char *error;
i_warning("SIGHUP received - reloading configuration");
/* see if hostname changed */
hostpid_init();
if (services->config->process_avail == 0) {
/* we can't reload config if there's no config process. */
if (service_process_create(services->config) == NULL) {
i_error("Can't reload configuration because "
"we couldn't create a config process");
return;
}
}
memset(&input, 0, sizeof(input));
input.roots = set_roots;
input.module = MASTER_SERVICE_NAME;
input.config_path = services_get_config_socket_path(services);
if (master_service_settings_read(master_service, &input,
&output, &error) < 0) {
i_error("Error reading configuration: %s", error);
return;
}
sets = master_service_settings_get_others(master_service);
set = sets[0];
if (services_create(set, &new_services, &error) < 0) {
/* new configuration is invalid, keep the old */
i_error("Config reload failed: %s", error);
return;
}
new_services->config->config_file_path =
p_strdup(new_services->pool,
services->config->config_file_path);
/* switch to new configuration. */
services_monitor_stop(services, FALSE);
if (services_listen_using(new_services, services) < 0) {
services_monitor_start(services);
return;
}
/* anvil never dies. it just gets moved to the new services list */
service = service_lookup_type(services, SERVICE_TYPE_ANVIL);
if (service != NULL) {
while (service->processes != NULL)
service_process_destroy(service->processes);
}
services_destroy(services, FALSE);
services = new_services;
services_monitor_start(services);
}
static void
sig_log_reopen(const siginfo_t *si ATTR_UNUSED, void *context ATTR_UNUSED)
{
service_signal(services->log, SIGUSR1);
master_service_init_log(master_service, "master: ");
i_set_fatal_handler(master_fatal_callback);
}
static void
sig_reap_children(const siginfo_t *si ATTR_UNUSED, void *context ATTR_UNUSED)
{
services_monitor_reap_children();
}
static void sig_die(const siginfo_t *si, void *context ATTR_UNUSED)
{
i_warning("Killed with signal %d (by pid=%s uid=%s code=%s)",
si->si_signo, dec2str(si->si_pid),
dec2str(si->si_uid),
lib_signal_code_to_str(si->si_signo, si->si_code));
master_service_stop(master_service);
}
static struct master_settings *master_settings_read(void)
{
struct master_service_settings_input input;
struct master_service_settings_output output;
const char *error;
memset(&input, 0, sizeof(input));
input.roots = set_roots;
input.module = "master";
input.parse_full_config = TRUE;
input.preserve_environment = TRUE;
if (master_service_settings_read(master_service, &input, &output,
&error) < 0)
i_fatal("Error reading configuration: %s", error);
return master_service_settings_get_others(master_service)[0];
}
static void master_set_import_environment(const struct master_settings *set)
{
const char *const *envs, *key, *value;
ARRAY_TYPE(const_string) keys;
if (*set->import_environment == '\0')
return;
t_array_init(&keys, 8);
envs = t_strsplit_spaces(set->import_environment, " ");
for (; *envs != NULL; envs++) {
value = strchr(*envs, '=');
if (value == NULL)
key = *envs;
else {
key = t_strdup_until(*envs, value);
env_put(*envs);
}
array_append(&keys, &key, 1);
}
(void)array_append_space(&keys);
value = t_strarray_join(array_idx(&keys, 0), " ");
env_put(t_strconcat(DOVECOT_PRESERVE_ENVS_ENV"=", value, NULL));
}
static void main_log_startup(void)
{
#define STARTUP_STRING PACKAGE_NAME" v"DOVECOT_VERSION_FULL" starting up"
rlim_t core_limit;
core_dumps_disabled = restrict_get_core_limit(&core_limit) == 0 &&
core_limit == 0;
if (core_dumps_disabled)
i_info(STARTUP_STRING" (core dumps disabled)");
else
i_info(STARTUP_STRING);
}
static void master_set_process_limit(void)
{
struct service *const *servicep;
unsigned int process_limit = 0;
rlim_t nproc;
/* we'll just count all the processes that can exist and set the
process limit so that we won't reach it. it's usually higher than
needed, since we'd only need to set it high enough for each
separate UID not to reach the limit, but this is difficult to
guess: mail processes should probably be counted together for a
common vmail user (unless system users are being used), but
we can't really guess what the mail processes are. */
array_foreach(&services->services, servicep)
process_limit += (*servicep)->process_limit;
if (restrict_get_process_limit(&nproc) == 0 &&
process_limit > nproc)
restrict_process_count(process_limit);
}
static void main_init(const struct master_settings *set)
{
master_set_process_limit();
drop_capabilities();
/* deny file access from everyone else except owner */
(void)umask(0077);
main_log_startup();
lib_signals_init();
lib_signals_ignore(SIGPIPE, TRUE);
lib_signals_ignore(SIGALRM, FALSE);
lib_signals_set_handler(SIGHUP, LIBSIG_FLAGS_SAFE,
sig_settings_reload, NULL);
lib_signals_set_handler(SIGUSR1, LIBSIG_FLAGS_SAFE,
sig_log_reopen, NULL);
lib_signals_set_handler(SIGCHLD, LIBSIG_FLAGS_SAFE,
sig_reap_children, NULL);
lib_signals_set_handler(SIGINT, LIBSIG_FLAGS_SAFE, sig_die, NULL);
lib_signals_set_handler(SIGTERM, LIBSIG_FLAGS_SAFE, sig_die, NULL);
create_pid_file(pidfile_path);
create_config_symlink(set);
services_monitor_start(services);
}
static void global_dead_pipe_close(void)
{
if (close(global_master_dead_pipe_fd[0]) < 0)
i_error("close(global dead pipe) failed: %m");
if (close(global_master_dead_pipe_fd[1]) < 0)
i_error("close(global dead pipe) failed: %m");
global_master_dead_pipe_fd[0] = -1;
global_master_dead_pipe_fd[1] = -1;
}
static void main_deinit(void)
{
/* kill services and wait for them to die before unlinking pid file */
global_dead_pipe_close();
services_destroy(services, TRUE);
if (unlink(pidfile_path) < 0)
i_error("unlink(%s) failed: %m", pidfile_path);
i_free(pidfile_path);
service_anvil_global_deinit();
service_pids_deinit();
}
static const char *get_full_config_path(struct service_list *list)
{
const char *path;
path = master_service_get_config_path(master_service);
if (*path == '/')
return path;
return p_strdup(list->pool, t_abspath(path));
}
static void master_time_moved(time_t old_time, time_t new_time)
{
unsigned long secs;
if (new_time >= old_time)
return;
/* time moved backwards. disable launching new service processes
until */
secs = old_time - new_time + 1;
if (secs > SERVICE_TIME_MOVED_BACKWARDS_MAX_THROTTLE_SECS)
secs = SERVICE_TIME_MOVED_BACKWARDS_MAX_THROTTLE_SECS;
services_throttle_time_sensitives(services, secs);
i_warning("Time moved backwards by %lu seconds, "
"waiting for %lu secs until new services are launched again.",
(unsigned long)(old_time - new_time), secs);
}
static void daemonize(void)
{
pid_t pid;
pid = fork();
if (pid < 0)
i_fatal("fork() failed: %m");
if (pid != 0)
_exit(0);
if (setsid() < 0)
i_fatal("setsid() failed: %m");
/* update my_pid */
hostpid_init();
}
static void print_help(void)
{
fprintf(stderr,
"Usage: dovecot [-F] [-c <config file>] [-p] [-n] [-a] [--help] [--version]\n"
" [--build-options] [reload] [stop]\n");
}
static void print_build_options(void)
{
printf("Build options:"
#ifdef IOLOOP_EPOLL
" ioloop=epoll"
#endif
#ifdef IOLOOP_KQUEUE
" ioloop=kqueue"
#endif
#ifdef IOLOOP_POLL
" ioloop=poll"
#endif
#ifdef IOLOOP_SELECT
" ioloop=select"
#endif
#ifdef IOLOOP_NOTIFY_DNOTIFY
" notify=dnotify"
#endif
#ifdef IOLOOP_NOTIFY_INOTIFY
" notify=inotify"
#endif
#ifdef IOLOOP_NOTIFY_KQUEUE
" notify=kqueue"
#endif
#ifdef HAVE_IPV6
" ipv6"
#endif
#ifdef HAVE_GNUTLS
" gnutls"
#endif
#ifdef HAVE_OPENSSL
" openssl"
#endif
" io_block_size=%u"
"\nMail storages: "MAIL_STORAGES"\n"
#ifdef SQL_DRIVER_PLUGINS
"SQL driver plugins:"
#else
"SQL drivers:"
#endif
#ifdef BUILD_MYSQL
" mysql"
#endif
#ifdef BUILD_PGSQL
" postgresql"
#endif
#ifdef BUILD_SQLITE
" sqlite"
#endif
"\nPassdb:"
#ifdef PASSDB_BSDAUTH
" bsdauth"
#endif
#ifdef PASSDB_CHECKPASSWORD
" checkpassword"
#endif
#ifdef PASSDB_LDAP
" ldap"
#endif
#ifdef PASSDB_PAM
" pam"
#endif
#ifdef PASSDB_PASSWD
" passwd"
#endif
#ifdef PASSDB_PASSWD_FILE
" passwd-file"
#endif
#ifdef PASSDB_SHADOW
" shadow"
#endif
#ifdef PASSDB_SQL
" sql"
#endif
#ifdef PASSDB_VPOPMAIL
" vpopmail"
#endif
"\nUserdb:"
#ifdef USERDB_CHECKPASSWORD
" checkpassword"
#endif
#ifdef USERDB_LDAP
" ldap"
#ifndef BUILTIN_LDAP
"(plugin)"
#endif
#endif
#ifdef USERDB_NSS
" nss"
#endif
#ifdef USERDB_PASSWD
" passwd"
#endif
#ifdef USERDB_PREFETCH
" prefetch"
#endif
#ifdef USERDB_PASSWD_FILE
" passwd-file"
#endif
#ifdef USERDB_SQL
" sql"
#endif
#ifdef USERDB_STATIC
" static"
#endif
#ifdef USERDB_VPOPMAIL
" vpopmail"
#endif
"\n", IO_BLOCK_SIZE);
}
int main(int argc, char *argv[])
{
struct master_settings *set;
const char *error, *doveconf_arg = NULL;
failure_callback_t *orig_info_callback, *orig_debug_callback;
bool foreground = FALSE, ask_key_pass = FALSE;
bool doubleopts[argc];
int i, c;
#ifdef DEBUG
if (getenv("GDB") == NULL)
fd_debug_verify_leaks(3, 1024);
#endif
/* drop -- prefix from all --args. ugly, but the only way that it
works with standard getopt() in all OSes.. */
for (i = 1; i < argc; i++) {
if (strncmp(argv[i], "--", 2) == 0) {
if (argv[i][2] == '\0')
break;
argv[i] += 2;
doubleopts[i] = TRUE;
} else {
doubleopts[i] = FALSE;
}
}
master_service = master_service_init(MASTER_SERVICE_NAME,
MASTER_SERVICE_FLAG_STANDALONE |
MASTER_SERVICE_FLAG_DONT_LOG_TO_STDERR,
&argc, &argv, "+Fanp");
i_set_failure_prefix("");
io_loop_set_time_moved_callback(current_ioloop, master_time_moved);
master_uid = geteuid();
master_gid = getegid();
while ((c = master_getopt(master_service)) > 0) {
switch (c) {
case 'F':
foreground = TRUE;
break;
case 'a':
doveconf_arg = "-a";
break;
case 'n':
doveconf_arg = "-n";
break;
case 'p':
/* Ask SSL private key password */
ask_key_pass = TRUE;
break;
default:
if (!master_service_parse_option(master_service,
c, optarg)) {
print_help();
exit(FATAL_DEFAULT);
}
break;
}
}
if (doveconf_arg != NULL) {
const char **args;
args = t_new(const char *, 5);
args[0] = DOVECOT_CONFIG_BIN_PATH;
args[1] = doveconf_arg;
args[2] = "-c";
args[3] = master_service_get_config_path(master_service);
args[4] = NULL;
execv_const(args[0], args);
}
if (optind == argc) {
/* starting Dovecot */
} else if (!doubleopts[optind]) {
/* dovecot xx -> doveadm xx */
(void)execv(BINDIR"/doveadm", argv);
i_fatal("execv("BINDIR"/doveadm) failed: %m");
} else if (strcmp(argv[optind], "version") == 0) {
printf("%s\n", DOVECOT_VERSION_FULL);
return 0;
} else if (strcmp(argv[optind], "build-options") == 0) {
print_build_options();
return 0;
} else if (strcmp(argv[optind], "log-error") == 0) {
fprintf(stderr, "Writing to error logs and killing myself..\n");
argv[optind] = "log test";
(void)execv(BINDIR"/doveadm", argv);
i_fatal("execv("BINDIR"/doveadm) failed: %m");
} else if (strcmp(argv[optind], "help") == 0) {
print_help();
return 0;
} else {
print_help();
i_fatal("Unknown argument: --%s", argv[optind]);
}
do {
null_fd = open("/dev/null", O_WRONLY);
if (null_fd == -1)
i_fatal("Can't open /dev/null: %m");
fd_close_on_exec(null_fd, TRUE);
} while (null_fd <= STDERR_FILENO);
if (pipe(global_master_dead_pipe_fd) < 0)
i_fatal("pipe() failed: %m");
fd_close_on_exec(global_master_dead_pipe_fd[0], TRUE);
fd_close_on_exec(global_master_dead_pipe_fd[1], TRUE);
set = master_settings_read();
if (ask_key_pass) {
ssl_manual_key_password =
t_askpass("Give the password for SSL keys: ");
}
if (dup2(null_fd, STDIN_FILENO) < 0 ||
dup2(null_fd, STDOUT_FILENO) < 0)
i_fatal("dup2(null_fd) failed: %m");
pidfile_path =
i_strconcat(set->base_dir, "/"MASTER_PID_FILE_NAME, NULL);
master_service_init_log(master_service, "master: ");
i_get_failure_handlers(&orig_fatal_callback, &orig_error_callback,
&orig_info_callback, &orig_debug_callback);
i_set_fatal_handler(startup_fatal_handler);
i_set_error_handler(startup_error_handler);
pid_file_check_running(pidfile_path);
master_settings_do_fixes(set);
fatal_log_check(set);
T_BEGIN {
master_set_import_environment(set);
} T_END;
master_service_env_clean();
/* create service structures from settings. if there are any errors in
service configuration we'll catch it here. */
service_pids_init();
service_anvil_global_init();
if (services_create(set, &services, &error) < 0)
i_fatal("%s", error);
services->config->config_file_path = get_full_config_path(services);
/* if any listening fails, fail completely */
if (services_listen(services) <= 0)
i_fatal("Failed to start listeners");
if (chdir(set->base_dir) < 0)
i_fatal("chdir(%s) failed: %m", set->base_dir);
if (strcmp(services->service_set->log_path, "/dev/stderr") != 0 &&
strcmp(services->service_set->info_log_path, "/dev/stderr") != 0 &&
strcmp(services->service_set->debug_log_path, "/dev/stderr") != 0) {
if (dup2(null_fd, STDERR_FILENO) < 0)
i_fatal("dup2(null_fd) failed: %m");
}
i_set_fatal_handler(master_fatal_callback);
i_set_error_handler(orig_error_callback);
if (!foreground)
daemonize();
main_init(set);
master_service_run(master_service, NULL);
main_deinit();
master_service_deinit(&master_service);
return 0;
}