summaryrefslogtreecommitdiffstats
path: root/src/master/service.h
blob: c6ea604e21cc5b0940fb2d3cee371f92a9f7de91 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
#ifndef SERVICE_H
#define SERVICE_H

#include "net.h"
#include "master-settings.h"

/* If a service process doesn't send its first status notification in
   this many seconds, kill the process */
#define SERVICE_FIRST_STATUS_TIMEOUT_SECS 30

#define SERVICE_STARTUP_FAILURE_THROTTLE_MIN_MSECS (2*1000)
#define SERVICE_STARTUP_FAILURE_THROTTLE_MAX_MSECS (60*1000)

enum service_listener_type {
	SERVICE_LISTENER_UNIX,
	SERVICE_LISTENER_FIFO,
	SERVICE_LISTENER_INET
};

struct service_listener {
	struct service *service;

	enum service_listener_type type;
	int fd; /* may be -1 */
	struct io *io;

	const char *name;
	const char *inet_address;

	union {
		struct {
			const struct file_listener_settings *set;
			uid_t uid;
			gid_t gid;
		} fileset;
		struct {
			const struct inet_listener_settings *set;
			struct ip_addr ip;
		} inetset;
	} set;

	bool reuse_port;
};

struct service {
	struct service_list *list;

	enum service_type type;

	const struct service_settings *set;
	const char *config_file_path;

	const char *executable;
	uid_t uid;
	gid_t gid;
	gid_t privileged_gid;
	const char *extra_gids; /* comma-separated list */

	/* all listeners, even those that aren't currently listening */
	ARRAY(struct service_listener *) listeners;
	/* linked list of processes belonging to this service, which have
	   idle_start == 0. */
	struct service_process *busy_processes;
	/* linked list of processes belonging to this service, which have
	   ldle_start != 0. */
	struct service_process *idle_processes_head, *idle_processes_tail;

	/* number of processes currently created for this service */
	unsigned int process_count;
	/* number of processes currently accepting new clients */
	unsigned int process_avail;
	/* number of processes currently idling (idle_start != 0) */
	unsigned int process_idling;
	/* Lowest number of processes that have been idling at the same time.
	   This is reset to process_idling every idle_kill seconds. */
	unsigned int process_idling_lowwater_since_kills;
	/* max number of processes allowed */
	unsigned int process_limit;
	/* Total number of processes ever created */
	uint64_t process_count_total;

	/* Maximum number of client connections a process can handle. */
	unsigned int client_limit;
	/* Kill idling processes after this many seconds. */
	unsigned int idle_kill;
	/* set->vsz_limit or set->master_set->default_client_limit */
	uoff_t vsz_limit;

	/* log process pipe file descriptors. */
	int log_fd[2];
	/* fd that log process sees log_fd[0] as. can be used to identify
	   service name when sending commands via master_log_fd. */
	int log_process_internal_fd;

	/* status report pipe file descriptors */
	int status_fd[2];
	struct io *io_status;

	int master_dead_pipe_fd[2];

	unsigned int throttle_msecs;
	time_t exit_failure_last;
	unsigned int exit_failures_in_sec;

	/* Login process's notify fd. We change its seek position to
	   communicate state to login processes. */
	int login_notify_fd;
	time_t last_login_notify_time;
	struct timeout *to_login_notify;

	/* if a process fails before servicing its first request, assume it's
	   broken and start throttling new process creations */
	struct timeout *to_throttle;
	/* when process_limit is reached, wait for a while until we actually
	   start dropping pending connections */
	struct timeout *to_drop;
	/* delayed process_limit reached warning with SERVICE_TYPE_WORKER */
	struct timeout *to_drop_warning;
	/* next time to try to kill idling processes */
	struct timeout *to_idle;

	/* prefork processes up to process_min_avail if there's time */
	struct timeout *to_prefork;
	unsigned int prefork_counter;

	/* Last time a "dropping client connections" warning was logged */
	time_t last_drop_warning;

	/* all processes are in use and new connections are coming */
	bool listen_pending:1;
	/* service is currently listening for new connections */
	bool listening:1;
	/* TRUE if service has at least one inet_listener */
	bool have_inet_listeners:1;
	/* service_login_notify()'s last notification state */
	bool last_login_full_notify:1;
	/* service has exited at least once with exit code 0 */
	bool have_successful_exits:1;
	/* service was stopped via doveadm */
	bool doveadm_stop:1;
};

struct service_list {
	pool_t pool;
	pool_t set_pool;
	int refcount;
	struct timeout *to_kill;
	unsigned int fork_counter;

	const struct master_settings *set;
	const struct master_service_settings *service_set;

	struct service *config;
	struct service *log;
	struct service *anvil;

	struct file_listener_settings master_listener_set;
	struct io *io_master;
	int master_fd;

	/* nonblocking log fds usd by master */
	int master_log_fd[2];
	struct service_process_notify *log_byes;

	ARRAY(struct service *) services;

	bool destroying:1;
	bool destroyed:1;
	bool sigterm_sent:1;
	bool sigterm_sent_to_log:1;
};

HASH_TABLE_DEFINE_TYPE(pid_process, void *, struct service_process *);
extern HASH_TABLE_TYPE(pid_process) service_pids;

/* Create all services from settings */
int services_create(const struct master_settings *set,
		    struct service_list **services_r, const char **error_r);

/* Destroy services */
void services_destroy(struct service_list *service_list, bool wait);

void service_list_ref(struct service_list *service_list);
void service_list_unref(struct service_list *service_list);

/* Return path to configuration process socket. */
const char *services_get_config_socket_path(struct service_list *service_list);

/* Send a signal to all processes in a given service. However, if we're sending
   a SIGTERM and a process hasn't yet sent the initial status notification,
   that process is skipped. The number of such skipped processes are stored in
   uninitialized_count_r. Returns the number of processes that a signal was
   successfully sent to. */
unsigned int service_signal(struct service *service, int signo,
			    unsigned int *uninitialized_count_r);
/* Notify all processes (if necessary) that no more connections can be handled
   by the service without killing existing connections (TRUE) or that they
   can be (FALSE). */
void service_login_notify(struct service *service, bool all_processes_full);

/* Prevent service from launching new processes for a while. */
void service_throttle(struct service *service, unsigned int msecs);
/* Time moved backwards. Throttle services that care about time. */
void services_throttle_time_sensitives(struct service_list *list,
				       unsigned int msecs);

/* Find service by name. */
struct service *
service_lookup(struct service_list *service_list, const char *name);
/* Find service by type */
struct service *
service_lookup_type(struct service_list *service_list, enum service_type type);

void service_error(struct service *service, const char *format, ...)
	ATTR_FORMAT(2, 3);

void service_pids_init(void);
void service_pids_deinit(void);

#endif