GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.sbin/httpd/control.c Lines: 2 145 1.4 %
Date: 2017-11-07 Branches: 1 84 1.2 %

Line Branch Exec Source
1
/*	$OpenBSD: control.c,v 1.13 2017/01/09 14:49:22 reyk Exp $	*/
2
3
/*
4
 * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
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 USE, DATA OR PROFITS, WHETHER IN AN
15
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17
 */
18
19
#include <sys/queue.h>
20
#include <sys/stat.h>
21
#include <sys/socket.h>
22
#include <sys/time.h>
23
#include <sys/un.h>
24
25
#include <errno.h>
26
#include <event.h>
27
#include <fcntl.h>
28
#include <stdlib.h>
29
#include <string.h>
30
#include <unistd.h>
31
#include <imsg.h>
32
33
#include "httpd.h"
34
35
#define	CONTROL_BACKLOG	5
36
37
struct ctl_connlist ctl_conns;
38
39
void		 control_accept(int, short, void *);
40
void		 control_close(int, struct control_sock *);
41
42
int
43
control_init(struct privsep *ps, struct control_sock *cs)
44
{
45
	struct httpd		*env = ps->ps_env;
46
	struct sockaddr_un	 sun;
47
	int			 fd;
48
	mode_t			 old_umask, mode;
49
50
	if (cs->cs_name == NULL)
51
		return (0);
52
53
	if ((fd = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0)) == -1) {
54
		log_warn("%s: socket", __func__);
55
		return (-1);
56
	}
57
58
	sun.sun_family = AF_UNIX;
59
	if (strlcpy(sun.sun_path, cs->cs_name,
60
	    sizeof(sun.sun_path)) >= sizeof(sun.sun_path)) {
61
		log_warn("%s: %s name too long", __func__, cs->cs_name);
62
		close(fd);
63
		return (-1);
64
	}
65
66
	if (unlink(cs->cs_name) == -1)
67
		if (errno != ENOENT) {
68
			log_warn("%s: unlink %s", __func__, cs->cs_name);
69
			close(fd);
70
			return (-1);
71
		}
72
73
	if (cs->cs_restricted) {
74
		old_umask = umask(S_IXUSR|S_IXGRP|S_IXOTH);
75
		mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH;
76
	} else {
77
		old_umask = umask(S_IXUSR|S_IXGRP|S_IWOTH|S_IROTH|S_IXOTH);
78
		mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP;
79
	}
80
81
	if (bind(fd, (struct sockaddr *)&sun, sizeof(sun)) == -1) {
82
		log_warn("%s: bind: %s", __func__, cs->cs_name);
83
		close(fd);
84
		(void)umask(old_umask);
85
		return (-1);
86
	}
87
	(void)umask(old_umask);
88
89
	if (chmod(cs->cs_name, mode) == -1) {
90
		log_warn("%s: chmod", __func__);
91
		close(fd);
92
		(void)unlink(cs->cs_name);
93
		return (-1);
94
	}
95
96
	cs->cs_fd = fd;
97
	cs->cs_env = env;
98
99
	return (0);
100
}
101
102
int
103
control_listen(struct control_sock *cs)
104
{
105
	if (cs->cs_name == NULL)
106
		return (0);
107
108
	if (listen(cs->cs_fd, CONTROL_BACKLOG) == -1) {
109
		log_warn("%s: listen", __func__);
110
		return (-1);
111
	}
112
113
	event_set(&cs->cs_ev, cs->cs_fd, EV_READ,
114
	    control_accept, cs);
115
	event_add(&cs->cs_ev, NULL);
116
	evtimer_set(&cs->cs_evt, control_accept, cs);
117
118
	return (0);
119
}
120
121
void
122
control_cleanup(struct control_sock *cs)
123
{
124
144
	if (cs->cs_name == NULL)
125
		return;
126
	event_del(&cs->cs_ev);
127
	event_del(&cs->cs_evt);
128
72
}
129
130
/* ARGSUSED */
131
void
132
control_accept(int listenfd, short event, void *arg)
133
{
134
	int			 connfd;
135
	socklen_t		 len;
136
	struct sockaddr_un	 sun;
137
	struct ctl_conn		*c;
138
	struct control_sock	*cs = arg;
139
140
	event_add(&cs->cs_ev, NULL);
141
	if ((event & EV_TIMEOUT))
142
		return;
143
144
	len = sizeof(sun);
145
	if ((connfd = accept4(listenfd,
146
	    (struct sockaddr *)&sun, &len, SOCK_NONBLOCK)) == -1) {
147
		/*
148
		 * Pause accept if we are out of file descriptors, or
149
		 * libevent will haunt us here too.
150
		 */
151
		if (errno == ENFILE || errno == EMFILE) {
152
			struct timeval evtpause = { 1, 0 };
153
154
			event_del(&cs->cs_ev);
155
			evtimer_add(&cs->cs_evt, &evtpause);
156
		} else if (errno != EWOULDBLOCK && errno != EINTR &&
157
		    errno != ECONNABORTED)
158
			log_warn("%s: accept", __func__);
159
		return;
160
	}
161
162
	if ((c = calloc(1, sizeof(struct ctl_conn))) == NULL) {
163
		close(connfd);
164
		log_warn("%s: calloc", __func__);
165
		return;
166
	}
167
168
	imsg_init(&c->iev.ibuf, connfd);
169
	c->iev.handler = control_dispatch_imsg;
170
	c->iev.events = EV_READ;
171
	c->iev.data = cs;	/* proc.c cheats (reuses the handler) */
172
	event_set(&c->iev.ev, c->iev.ibuf.fd, c->iev.events,
173
	    c->iev.handler, cs);
174
	event_add(&c->iev.ev, NULL);
175
176
	TAILQ_INSERT_TAIL(&ctl_conns, c, entry);
177
}
178
179
struct ctl_conn *
180
control_connbyfd(int fd)
181
{
182
	struct ctl_conn	*c;
183
184
	TAILQ_FOREACH(c, &ctl_conns, entry) {
185
		if (c->iev.ibuf.fd == fd)
186
			break;
187
	}
188
189
	return (c);
190
}
191
192
void
193
control_close(int fd, struct control_sock *cs)
194
{
195
	struct ctl_conn	*c;
196
197
	if ((c = control_connbyfd(fd)) == NULL) {
198
		log_warn("%s: fd %d not found", __func__, fd);
199
		return;
200
	}
201
202
	msgbuf_clear(&c->iev.ibuf.w);
203
	TAILQ_REMOVE(&ctl_conns, c, entry);
204
205
	event_del(&c->iev.ev);
206
	close(c->iev.ibuf.fd);
207
208
	/* Some file descriptors are available again. */
209
	if (evtimer_pending(&cs->cs_evt, NULL)) {
210
		evtimer_del(&cs->cs_evt);
211
		event_add(&cs->cs_ev, NULL);
212
	}
213
214
	free(c);
215
}
216
217
/* ARGSUSED */
218
void
219
control_dispatch_imsg(int fd, short event, void *arg)
220
{
221
	struct control_sock	*cs = arg;
222
	struct ctl_conn		*c;
223
	struct imsg		 imsg;
224
	int			 n;
225
	int			 verbose;
226
	struct httpd		*env = cs->cs_env;
227
228
	if ((c = control_connbyfd(fd)) == NULL) {
229
		log_warn("%s: fd %d not found", __func__, fd);
230
		return;
231
	}
232
233
	if (event & EV_READ) {
234
		if (((n = imsg_read(&c->iev.ibuf)) == -1 && errno != EAGAIN) ||
235
		    n == 0) {
236
			control_close(fd, cs);
237
			return;
238
		}
239
	}
240
241
	if (event & EV_WRITE) {
242
		if (msgbuf_write(&c->iev.ibuf.w) <= 0 && errno != EAGAIN) {
243
			control_close(fd, cs);
244
			return;
245
		}
246
	}
247
248
	for (;;) {
249
		if ((n = imsg_get(&c->iev.ibuf, &imsg)) == -1) {
250
			control_close(fd, cs);
251
			return;
252
		}
253
254
		if (n == 0)
255
			break;
256
257
		if (c->waiting) {
258
			log_debug("%s: unexpected imsg %d",
259
			    __func__, imsg.hdr.type);
260
			imsg_free(&imsg);
261
			control_close(fd, cs);
262
			return;
263
		}
264
265
		switch (imsg.hdr.type) {
266
		case IMSG_CTL_SHUTDOWN:
267
		case IMSG_CTL_RELOAD:
268
		case IMSG_CTL_REOPEN:
269
			proc_forward_imsg(env->sc_ps, &imsg, PROC_PARENT, -1);
270
			break;
271
		case IMSG_CTL_NOTIFY:
272
			if (c->flags & CTL_CONN_NOTIFY) {
273
				log_debug("%s: "
274
				    "client requested notify more than once",
275
				    __func__);
276
				imsg_compose_event(&c->iev, IMSG_CTL_FAIL,
277
				    0, env->sc_ps->ps_instance + 1, -1,
278
				    NULL, 0);
279
				break;
280
			}
281
			c->flags |= CTL_CONN_NOTIFY;
282
			break;
283
		case IMSG_CTL_VERBOSE:
284
			IMSG_SIZE_CHECK(&imsg, &verbose);
285
286
			memcpy(&verbose, imsg.data, sizeof(verbose));
287
288
			proc_forward_imsg(env->sc_ps, &imsg, PROC_PARENT, -1);
289
			proc_forward_imsg(env->sc_ps, &imsg, PROC_SERVER, -1);
290
291
			memcpy(imsg.data, &verbose, sizeof(verbose));
292
			control_imsg_forward(env->sc_ps, &imsg);
293
			log_setverbose(verbose);
294
			break;
295
		default:
296
			log_debug("%s: error handling imsg %d",
297
			    __func__, imsg.hdr.type);
298
			break;
299
		}
300
		imsg_free(&imsg);
301
	}
302
303
	imsg_event_add(&c->iev);
304
}
305
306
void
307
control_imsg_forward(struct privsep *ps, struct imsg *imsg)
308
{
309
	struct ctl_conn *c;
310
311
	TAILQ_FOREACH(c, &ctl_conns, entry)
312
		if (c->flags & CTL_CONN_NOTIFY)
313
			imsg_compose_event(&c->iev, imsg->hdr.type,
314
			    0, ps->ps_instance + 1, -1, imsg->data,
315
			    imsg->hdr.len - IMSG_HEADER_SIZE);
316
}