1255570Strasz/*-
2255570Strasz * Copyright (c) 2012 The FreeBSD Foundation
3255570Strasz * All rights reserved.
4255570Strasz *
5255570Strasz * This software was developed by Edward Tomasz Napierala under sponsorship
6255570Strasz * from the FreeBSD Foundation.
7255570Strasz *
8255570Strasz * Redistribution and use in source and binary forms, with or without
9255570Strasz * modification, are permitted provided that the following conditions
10255570Strasz * are met:
11255570Strasz * 1. Redistributions of source code must retain the above copyright
12255570Strasz *    notice, this list of conditions and the following disclaimer.
13255570Strasz * 2. Redistributions in binary form must reproduce the above copyright
14255570Strasz *    notice, this list of conditions and the following disclaimer in the
15255570Strasz *    documentation and/or other materials provided with the distribution.
16255570Strasz *
17255570Strasz * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18255570Strasz * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19255570Strasz * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20255570Strasz * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21255570Strasz * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22255570Strasz * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23255570Strasz * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24255570Strasz * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25255570Strasz * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26255570Strasz * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27255570Strasz * SUCH DAMAGE.
28255570Strasz *
29255570Strasz */
30255570Strasz
31270888Strasz#include <sys/cdefs.h>
32270888Strasz__FBSDID("$FreeBSD$");
33270888Strasz
34255570Strasz#include <sys/types.h>
35255570Strasz#include <sys/time.h>
36255570Strasz#include <sys/socket.h>
37255570Strasz#include <sys/wait.h>
38255570Strasz#include <netinet/in.h>
39270137Smav#include <arpa/inet.h>
40255570Strasz#include <assert.h>
41255570Strasz#include <ctype.h>
42255570Strasz#include <errno.h>
43255570Strasz#include <netdb.h>
44255570Strasz#include <signal.h>
45255570Strasz#include <stdbool.h>
46255570Strasz#include <stdio.h>
47255570Strasz#include <stdint.h>
48255570Strasz#include <stdlib.h>
49255570Strasz#include <string.h>
50255570Strasz#include <unistd.h>
51255570Strasz
52255570Strasz#include "ctld.h"
53255570Strasz
54265507Straszbool proxy_mode = false;
55265507Strasz
56255570Straszstatic volatile bool sighup_received = false;
57255570Straszstatic volatile bool sigterm_received = false;
58255570Straszstatic volatile bool sigalrm_received = false;
59255570Strasz
60255570Straszstatic int nchildren = 0;
61255570Strasz
62255570Straszstatic void
63255570Straszusage(void)
64255570Strasz{
65255570Strasz
66255570Strasz	fprintf(stderr, "usage: ctld [-d][-f config-file]\n");
67255570Strasz	exit(1);
68255570Strasz}
69255570Strasz
70255570Straszchar *
71255570Straszchecked_strdup(const char *s)
72255570Strasz{
73255570Strasz	char *c;
74255570Strasz
75255570Strasz	c = strdup(s);
76255570Strasz	if (c == NULL)
77255570Strasz		log_err(1, "strdup");
78255570Strasz	return (c);
79255570Strasz}
80255570Strasz
81255570Straszstruct conf *
82255570Straszconf_new(void)
83255570Strasz{
84255570Strasz	struct conf *conf;
85255570Strasz
86255570Strasz	conf = calloc(1, sizeof(*conf));
87255570Strasz	if (conf == NULL)
88255570Strasz		log_err(1, "calloc");
89255570Strasz	TAILQ_INIT(&conf->conf_targets);
90255570Strasz	TAILQ_INIT(&conf->conf_auth_groups);
91255570Strasz	TAILQ_INIT(&conf->conf_portal_groups);
92255570Strasz
93255570Strasz	conf->conf_debug = 0;
94255570Strasz	conf->conf_timeout = 60;
95255570Strasz	conf->conf_maxproc = 30;
96255570Strasz
97255570Strasz	return (conf);
98255570Strasz}
99255570Strasz
100255570Straszvoid
101255570Straszconf_delete(struct conf *conf)
102255570Strasz{
103255570Strasz	struct target *targ, *tmp;
104255570Strasz	struct auth_group *ag, *cagtmp;
105255570Strasz	struct portal_group *pg, *cpgtmp;
106255570Strasz
107255570Strasz	assert(conf->conf_pidfh == NULL);
108255570Strasz
109255570Strasz	TAILQ_FOREACH_SAFE(targ, &conf->conf_targets, t_next, tmp)
110255570Strasz		target_delete(targ);
111255570Strasz	TAILQ_FOREACH_SAFE(ag, &conf->conf_auth_groups, ag_next, cagtmp)
112255570Strasz		auth_group_delete(ag);
113255570Strasz	TAILQ_FOREACH_SAFE(pg, &conf->conf_portal_groups, pg_next, cpgtmp)
114255570Strasz		portal_group_delete(pg);
115255570Strasz	free(conf->conf_pidfile_path);
116255570Strasz	free(conf);
117255570Strasz}
118255570Strasz
119255570Straszstatic struct auth *
120255570Straszauth_new(struct auth_group *ag)
121255570Strasz{
122255570Strasz	struct auth *auth;
123255570Strasz
124255570Strasz	auth = calloc(1, sizeof(*auth));
125255570Strasz	if (auth == NULL)
126255570Strasz		log_err(1, "calloc");
127255570Strasz	auth->a_auth_group = ag;
128255570Strasz	TAILQ_INSERT_TAIL(&ag->ag_auths, auth, a_next);
129255570Strasz	return (auth);
130255570Strasz}
131255570Strasz
132255570Straszstatic void
133255570Straszauth_delete(struct auth *auth)
134255570Strasz{
135255570Strasz	TAILQ_REMOVE(&auth->a_auth_group->ag_auths, auth, a_next);
136255570Strasz
137255570Strasz	free(auth->a_user);
138255570Strasz	free(auth->a_secret);
139255570Strasz	free(auth->a_mutual_user);
140255570Strasz	free(auth->a_mutual_secret);
141255570Strasz	free(auth);
142255570Strasz}
143255570Strasz
144255570Straszconst struct auth *
145265514Straszauth_find(const struct auth_group *ag, const char *user)
146255570Strasz{
147255570Strasz	const struct auth *auth;
148255570Strasz
149255570Strasz	TAILQ_FOREACH(auth, &ag->ag_auths, a_next) {
150255570Strasz		if (strcmp(auth->a_user, user) == 0)
151255570Strasz			return (auth);
152255570Strasz	}
153255570Strasz
154255570Strasz	return (NULL);
155255570Strasz}
156255570Strasz
157263721Straszstatic void
158263721Straszauth_check_secret_length(struct auth *auth)
159263721Strasz{
160263721Strasz	size_t len;
161263721Strasz
162263721Strasz	len = strlen(auth->a_secret);
163263721Strasz	if (len > 16) {
164263721Strasz		if (auth->a_auth_group->ag_name != NULL)
165263721Strasz			log_warnx("secret for user \"%s\", auth-group \"%s\", "
166263721Strasz			    "is too long; it should be at most 16 characters "
167263721Strasz			    "long", auth->a_user, auth->a_auth_group->ag_name);
168263721Strasz		else
169263721Strasz			log_warnx("secret for user \"%s\", target \"%s\", "
170263721Strasz			    "is too long; it should be at most 16 characters "
171263721Strasz			    "long", auth->a_user,
172263723Strasz			    auth->a_auth_group->ag_target->t_name);
173263721Strasz	}
174263721Strasz	if (len < 12) {
175263721Strasz		if (auth->a_auth_group->ag_name != NULL)
176263721Strasz			log_warnx("secret for user \"%s\", auth-group \"%s\", "
177263721Strasz			    "is too short; it should be at least 12 characters "
178263721Strasz			    "long", auth->a_user,
179263721Strasz			    auth->a_auth_group->ag_name);
180263721Strasz		else
181263721Strasz			log_warnx("secret for user \"%s\", target \"%s\", "
182263721Strasz			    "is too short; it should be at least 16 characters "
183263721Strasz			    "long", auth->a_user,
184263723Strasz			    auth->a_auth_group->ag_target->t_name);
185263721Strasz	}
186263721Strasz
187263721Strasz	if (auth->a_mutual_secret != NULL) {
188263721Strasz		len = strlen(auth->a_secret);
189263721Strasz		if (len > 16) {
190263721Strasz			if (auth->a_auth_group->ag_name != NULL)
191263721Strasz				log_warnx("mutual secret for user \"%s\", "
192263721Strasz				    "auth-group \"%s\", is too long; it should "
193263721Strasz				    "be at most 16 characters long",
194263721Strasz				    auth->a_user, auth->a_auth_group->ag_name);
195263721Strasz			else
196263721Strasz				log_warnx("mutual secret for user \"%s\", "
197263721Strasz				    "target \"%s\", is too long; it should "
198263721Strasz				    "be at most 16 characters long",
199263721Strasz				    auth->a_user,
200263723Strasz				    auth->a_auth_group->ag_target->t_name);
201263721Strasz		}
202263721Strasz		if (len < 12) {
203263721Strasz			if (auth->a_auth_group->ag_name != NULL)
204263721Strasz				log_warnx("mutual secret for user \"%s\", "
205263721Strasz				    "auth-group \"%s\", is too short; it "
206263721Strasz				    "should be at least 12 characters long",
207263721Strasz				    auth->a_user, auth->a_auth_group->ag_name);
208263721Strasz			else
209263721Strasz				log_warnx("mutual secret for user \"%s\", "
210263721Strasz				    "target \"%s\", is too short; it should be "
211263721Strasz				    "at least 16 characters long",
212263721Strasz				    auth->a_user,
213263723Strasz				    auth->a_auth_group->ag_target->t_name);
214263721Strasz		}
215263721Strasz	}
216263721Strasz}
217263721Strasz
218263721Straszconst struct auth *
219263721Straszauth_new_chap(struct auth_group *ag, const char *user,
220263721Strasz    const char *secret)
221263721Strasz{
222263721Strasz	struct auth *auth;
223263721Strasz
224263721Strasz	if (ag->ag_type == AG_TYPE_UNKNOWN)
225263721Strasz		ag->ag_type = AG_TYPE_CHAP;
226263721Strasz	if (ag->ag_type != AG_TYPE_CHAP) {
227263721Strasz		if (ag->ag_name != NULL)
228263721Strasz			log_warnx("cannot mix \"chap\" authentication with "
229263721Strasz			    "other types for auth-group \"%s\"", ag->ag_name);
230263721Strasz		else
231263721Strasz			log_warnx("cannot mix \"chap\" authentication with "
232263721Strasz			    "other types for target \"%s\"",
233263723Strasz			    ag->ag_target->t_name);
234263721Strasz		return (NULL);
235263721Strasz	}
236263721Strasz
237263721Strasz	auth = auth_new(ag);
238263721Strasz	auth->a_user = checked_strdup(user);
239263721Strasz	auth->a_secret = checked_strdup(secret);
240263721Strasz
241263721Strasz	auth_check_secret_length(auth);
242263721Strasz
243263721Strasz	return (auth);
244263721Strasz}
245263721Strasz
246263721Straszconst struct auth *
247263721Straszauth_new_chap_mutual(struct auth_group *ag, const char *user,
248263721Strasz    const char *secret, const char *user2, const char *secret2)
249263721Strasz{
250263721Strasz	struct auth *auth;
251263721Strasz
252263721Strasz	if (ag->ag_type == AG_TYPE_UNKNOWN)
253263721Strasz		ag->ag_type = AG_TYPE_CHAP_MUTUAL;
254263721Strasz	if (ag->ag_type != AG_TYPE_CHAP_MUTUAL) {
255263721Strasz		if (ag->ag_name != NULL)
256263721Strasz			log_warnx("cannot mix \"chap-mutual\" authentication "
257263721Strasz			    "with other types for auth-group \"%s\"",
258263721Strasz			    ag->ag_name);
259263721Strasz		else
260263721Strasz			log_warnx("cannot mix \"chap-mutual\" authentication "
261263721Strasz			    "with other types for target \"%s\"",
262263723Strasz			    ag->ag_target->t_name);
263263721Strasz		return (NULL);
264263721Strasz	}
265263721Strasz
266263721Strasz	auth = auth_new(ag);
267263721Strasz	auth->a_user = checked_strdup(user);
268263721Strasz	auth->a_secret = checked_strdup(secret);
269263721Strasz	auth->a_mutual_user = checked_strdup(user2);
270263721Strasz	auth->a_mutual_secret = checked_strdup(secret2);
271263721Strasz
272263721Strasz	auth_check_secret_length(auth);
273263721Strasz
274263721Strasz	return (auth);
275263721Strasz}
276263721Strasz
277263720Straszconst struct auth_name *
278263720Straszauth_name_new(struct auth_group *ag, const char *name)
279263720Strasz{
280263720Strasz	struct auth_name *an;
281263720Strasz
282263720Strasz	an = calloc(1, sizeof(*an));
283263720Strasz	if (an == NULL)
284263720Strasz		log_err(1, "calloc");
285263720Strasz	an->an_auth_group = ag;
286263720Strasz	an->an_initator_name = checked_strdup(name);
287263720Strasz	TAILQ_INSERT_TAIL(&ag->ag_names, an, an_next);
288263720Strasz	return (an);
289263720Strasz}
290263720Strasz
291263720Straszstatic void
292263720Straszauth_name_delete(struct auth_name *an)
293263720Strasz{
294263720Strasz	TAILQ_REMOVE(&an->an_auth_group->ag_names, an, an_next);
295263720Strasz
296263720Strasz	free(an->an_initator_name);
297263720Strasz	free(an);
298263720Strasz}
299263720Strasz
300263720Straszbool
301263720Straszauth_name_defined(const struct auth_group *ag)
302263720Strasz{
303263720Strasz	if (TAILQ_EMPTY(&ag->ag_names))
304263720Strasz		return (false);
305263720Strasz	return (true);
306263720Strasz}
307263720Strasz
308263720Straszconst struct auth_name *
309263720Straszauth_name_find(const struct auth_group *ag, const char *name)
310263720Strasz{
311263720Strasz	const struct auth_name *auth_name;
312263720Strasz
313263720Strasz	TAILQ_FOREACH(auth_name, &ag->ag_names, an_next) {
314263720Strasz		if (strcmp(auth_name->an_initator_name, name) == 0)
315263720Strasz			return (auth_name);
316263720Strasz	}
317263720Strasz
318263720Strasz	return (NULL);
319263720Strasz}
320263720Strasz
321263720Straszconst struct auth_portal *
322263720Straszauth_portal_new(struct auth_group *ag, const char *portal)
323263720Strasz{
324263720Strasz	struct auth_portal *ap;
325270137Smav	char *net, *mask, *str, *tmp;
326270137Smav	int len, dm, m;
327263720Strasz
328263720Strasz	ap = calloc(1, sizeof(*ap));
329263720Strasz	if (ap == NULL)
330263720Strasz		log_err(1, "calloc");
331263720Strasz	ap->ap_auth_group = ag;
332263720Strasz	ap->ap_initator_portal = checked_strdup(portal);
333270137Smav	mask = str = checked_strdup(portal);
334270137Smav	net = strsep(&mask, "/");
335270137Smav	if (net[0] == '[')
336270137Smav		net++;
337270137Smav	len = strlen(net);
338270137Smav	if (len == 0)
339270137Smav		goto error;
340270137Smav	if (net[len - 1] == ']')
341270137Smav		net[len - 1] = 0;
342270137Smav	if (strchr(net, ':') != NULL) {
343270137Smav		struct sockaddr_in6 *sin6 =
344270137Smav		    (struct sockaddr_in6 *)&ap->ap_sa;
345270137Smav
346270137Smav		sin6->sin6_len = sizeof(*sin6);
347270137Smav		sin6->sin6_family = AF_INET6;
348270137Smav		if (inet_pton(AF_INET6, net, &sin6->sin6_addr) <= 0)
349270137Smav			goto error;
350270137Smav		dm = 128;
351270137Smav	} else {
352270137Smav		struct sockaddr_in *sin =
353270137Smav		    (struct sockaddr_in *)&ap->ap_sa;
354270137Smav
355270137Smav		sin->sin_len = sizeof(*sin);
356270137Smav		sin->sin_family = AF_INET;
357270137Smav		if (inet_pton(AF_INET, net, &sin->sin_addr) <= 0)
358270137Smav			goto error;
359270137Smav		dm = 32;
360270137Smav	}
361270137Smav	if (mask != NULL) {
362270137Smav		m = strtol(mask, &tmp, 0);
363270137Smav		if (m < 0 || m > dm || tmp[0] != 0)
364270137Smav			goto error;
365270137Smav	} else
366270137Smav		m = dm;
367270137Smav	ap->ap_mask = m;
368270137Smav	free(str);
369263720Strasz	TAILQ_INSERT_TAIL(&ag->ag_portals, ap, ap_next);
370263720Strasz	return (ap);
371270137Smav
372270137Smaverror:
373270137Smav	log_errx(1, "Incorrect initiator portal '%s'", portal);
374270137Smav	return (NULL);
375263720Strasz}
376263720Strasz
377263720Straszstatic void
378263720Straszauth_portal_delete(struct auth_portal *ap)
379263720Strasz{
380263720Strasz	TAILQ_REMOVE(&ap->ap_auth_group->ag_portals, ap, ap_next);
381263720Strasz
382263720Strasz	free(ap->ap_initator_portal);
383263720Strasz	free(ap);
384263720Strasz}
385263720Strasz
386263720Straszbool
387263720Straszauth_portal_defined(const struct auth_group *ag)
388263720Strasz{
389263720Strasz	if (TAILQ_EMPTY(&ag->ag_portals))
390263720Strasz		return (false);
391263720Strasz	return (true);
392263720Strasz}
393263720Strasz
394263720Straszconst struct auth_portal *
395270137Smavauth_portal_find(const struct auth_group *ag, const struct sockaddr_storage *ss)
396263720Strasz{
397270137Smav	const struct auth_portal *ap;
398270137Smav	const uint8_t *a, *b;
399270137Smav	int i;
400270137Smav	uint8_t bmask;
401263720Strasz
402270137Smav	TAILQ_FOREACH(ap, &ag->ag_portals, ap_next) {
403270137Smav		if (ap->ap_sa.ss_family != ss->ss_family)
404270137Smav			continue;
405270137Smav		if (ss->ss_family == AF_INET) {
406270137Smav			a = (const uint8_t *)
407270137Smav			    &((const struct sockaddr_in *)ss)->sin_addr;
408270137Smav			b = (const uint8_t *)
409270137Smav			    &((const struct sockaddr_in *)&ap->ap_sa)->sin_addr;
410270137Smav		} else {
411270137Smav			a = (const uint8_t *)
412270137Smav			    &((const struct sockaddr_in6 *)ss)->sin6_addr;
413270137Smav			b = (const uint8_t *)
414270137Smav			    &((const struct sockaddr_in6 *)&ap->ap_sa)->sin6_addr;
415270137Smav		}
416270137Smav		for (i = 0; i < ap->ap_mask / 8; i++) {
417270137Smav			if (a[i] != b[i])
418270137Smav				goto next;
419270137Smav		}
420270137Smav		if (ap->ap_mask % 8) {
421270137Smav			bmask = 0xff << (8 - (ap->ap_mask % 8));
422270137Smav			if ((a[i] & bmask) != (b[i] & bmask))
423270137Smav				goto next;
424270137Smav		}
425270137Smav		return (ap);
426270137Smavnext:
427270137Smav		;
428263720Strasz	}
429263720Strasz
430263720Strasz	return (NULL);
431263720Strasz}
432263720Strasz
433255570Straszstruct auth_group *
434255570Straszauth_group_new(struct conf *conf, const char *name)
435255570Strasz{
436255570Strasz	struct auth_group *ag;
437255570Strasz
438255570Strasz	if (name != NULL) {
439255570Strasz		ag = auth_group_find(conf, name);
440255570Strasz		if (ag != NULL) {
441255570Strasz			log_warnx("duplicated auth-group \"%s\"", name);
442255570Strasz			return (NULL);
443255570Strasz		}
444255570Strasz	}
445255570Strasz
446255570Strasz	ag = calloc(1, sizeof(*ag));
447255570Strasz	if (ag == NULL)
448255570Strasz		log_err(1, "calloc");
449255570Strasz	if (name != NULL)
450255570Strasz		ag->ag_name = checked_strdup(name);
451255570Strasz	TAILQ_INIT(&ag->ag_auths);
452263720Strasz	TAILQ_INIT(&ag->ag_names);
453263720Strasz	TAILQ_INIT(&ag->ag_portals);
454255570Strasz	ag->ag_conf = conf;
455255570Strasz	TAILQ_INSERT_TAIL(&conf->conf_auth_groups, ag, ag_next);
456255570Strasz
457255570Strasz	return (ag);
458255570Strasz}
459255570Strasz
460255570Straszvoid
461255570Straszauth_group_delete(struct auth_group *ag)
462255570Strasz{
463263720Strasz	struct auth *auth, *auth_tmp;
464263720Strasz	struct auth_name *auth_name, *auth_name_tmp;
465263720Strasz	struct auth_portal *auth_portal, *auth_portal_tmp;
466255570Strasz
467255570Strasz	TAILQ_REMOVE(&ag->ag_conf->conf_auth_groups, ag, ag_next);
468255570Strasz
469263720Strasz	TAILQ_FOREACH_SAFE(auth, &ag->ag_auths, a_next, auth_tmp)
470255570Strasz		auth_delete(auth);
471263720Strasz	TAILQ_FOREACH_SAFE(auth_name, &ag->ag_names, an_next, auth_name_tmp)
472263720Strasz		auth_name_delete(auth_name);
473263720Strasz	TAILQ_FOREACH_SAFE(auth_portal, &ag->ag_portals, ap_next,
474263720Strasz	    auth_portal_tmp)
475263720Strasz		auth_portal_delete(auth_portal);
476255570Strasz	free(ag->ag_name);
477255570Strasz	free(ag);
478255570Strasz}
479255570Strasz
480255570Straszstruct auth_group *
481265514Straszauth_group_find(const struct conf *conf, const char *name)
482255570Strasz{
483255570Strasz	struct auth_group *ag;
484255570Strasz
485255570Strasz	TAILQ_FOREACH(ag, &conf->conf_auth_groups, ag_next) {
486255570Strasz		if (ag->ag_name != NULL && strcmp(ag->ag_name, name) == 0)
487255570Strasz			return (ag);
488255570Strasz	}
489255570Strasz
490255570Strasz	return (NULL);
491255570Strasz}
492255570Strasz
493263724Straszstatic int
494263724Straszauth_group_set_type(struct auth_group *ag, int type)
495263724Strasz{
496263724Strasz
497263724Strasz	if (ag->ag_type == AG_TYPE_UNKNOWN) {
498263724Strasz		ag->ag_type = type;
499263724Strasz		return (0);
500263724Strasz	}
501263724Strasz
502263724Strasz	if (ag->ag_type == type)
503263724Strasz		return (0);
504263724Strasz
505263724Strasz	return (1);
506263724Strasz}
507263724Strasz
508263724Straszint
509263724Straszauth_group_set_type_str(struct auth_group *ag, const char *str)
510263724Strasz{
511263724Strasz	int error, type;
512263724Strasz
513263724Strasz	if (strcmp(str, "none") == 0) {
514263724Strasz		type = AG_TYPE_NO_AUTHENTICATION;
515263729Strasz	} else if (strcmp(str, "deny") == 0) {
516263729Strasz		type = AG_TYPE_DENY;
517263724Strasz	} else if (strcmp(str, "chap") == 0) {
518263724Strasz		type = AG_TYPE_CHAP;
519263724Strasz	} else if (strcmp(str, "chap-mutual") == 0) {
520263724Strasz		type = AG_TYPE_CHAP_MUTUAL;
521263724Strasz	} else {
522263724Strasz		if (ag->ag_name != NULL)
523263724Strasz			log_warnx("invalid auth-type \"%s\" for auth-group "
524263724Strasz			    "\"%s\"", str, ag->ag_name);
525263724Strasz		else
526263724Strasz			log_warnx("invalid auth-type \"%s\" for target "
527263724Strasz			    "\"%s\"", str, ag->ag_target->t_name);
528263724Strasz		return (1);
529263724Strasz	}
530263724Strasz
531263724Strasz	error = auth_group_set_type(ag, type);
532263724Strasz	if (error != 0) {
533263724Strasz		if (ag->ag_name != NULL)
534263724Strasz			log_warnx("cannot set auth-type to \"%s\" for "
535263724Strasz			    "auth-group \"%s\"; already has a different "
536263724Strasz			    "type", str, ag->ag_name);
537263724Strasz		else
538263724Strasz			log_warnx("cannot set auth-type to \"%s\" for target "
539263724Strasz			    "\"%s\"; already has a different type",
540263724Strasz			    str, ag->ag_target->t_name);
541263724Strasz		return (1);
542263724Strasz	}
543263724Strasz
544263724Strasz	return (error);
545263724Strasz}
546263724Strasz
547255570Straszstatic struct portal *
548255570Straszportal_new(struct portal_group *pg)
549255570Strasz{
550255570Strasz	struct portal *portal;
551255570Strasz
552255570Strasz	portal = calloc(1, sizeof(*portal));
553255570Strasz	if (portal == NULL)
554255570Strasz		log_err(1, "calloc");
555255570Strasz	TAILQ_INIT(&portal->p_targets);
556255570Strasz	portal->p_portal_group = pg;
557255570Strasz	TAILQ_INSERT_TAIL(&pg->pg_portals, portal, p_next);
558255570Strasz	return (portal);
559255570Strasz}
560255570Strasz
561255570Straszstatic void
562255570Straszportal_delete(struct portal *portal)
563255570Strasz{
564271632Strasz
565255570Strasz	TAILQ_REMOVE(&portal->p_portal_group->pg_portals, portal, p_next);
566271632Strasz	if (portal->p_ai != NULL)
567271632Strasz		freeaddrinfo(portal->p_ai);
568255570Strasz	free(portal->p_listen);
569255570Strasz	free(portal);
570255570Strasz}
571255570Strasz
572255570Straszstruct portal_group *
573255570Straszportal_group_new(struct conf *conf, const char *name)
574255570Strasz{
575255570Strasz	struct portal_group *pg;
576255570Strasz
577255678Strasz	pg = portal_group_find(conf, name);
578255678Strasz	if (pg != NULL) {
579255678Strasz		log_warnx("duplicated portal-group \"%s\"", name);
580255678Strasz		return (NULL);
581255570Strasz	}
582255570Strasz
583255570Strasz	pg = calloc(1, sizeof(*pg));
584255570Strasz	if (pg == NULL)
585255570Strasz		log_err(1, "calloc");
586255570Strasz	pg->pg_name = checked_strdup(name);
587255570Strasz	TAILQ_INIT(&pg->pg_portals);
588255570Strasz	pg->pg_conf = conf;
589255570Strasz	conf->conf_last_portal_group_tag++;
590255570Strasz	pg->pg_tag = conf->conf_last_portal_group_tag;
591255570Strasz	TAILQ_INSERT_TAIL(&conf->conf_portal_groups, pg, pg_next);
592255570Strasz
593255570Strasz	return (pg);
594255570Strasz}
595255570Strasz
596255570Straszvoid
597255570Straszportal_group_delete(struct portal_group *pg)
598255570Strasz{
599255570Strasz	struct portal *portal, *tmp;
600255570Strasz
601255570Strasz	TAILQ_REMOVE(&pg->pg_conf->conf_portal_groups, pg, pg_next);
602255570Strasz
603255570Strasz	TAILQ_FOREACH_SAFE(portal, &pg->pg_portals, p_next, tmp)
604255570Strasz		portal_delete(portal);
605255570Strasz	free(pg->pg_name);
606255570Strasz	free(pg);
607255570Strasz}
608255570Strasz
609255570Straszstruct portal_group *
610265514Straszportal_group_find(const struct conf *conf, const char *name)
611255570Strasz{
612255570Strasz	struct portal_group *pg;
613255570Strasz
614255570Strasz	TAILQ_FOREACH(pg, &conf->conf_portal_groups, pg_next) {
615255570Strasz		if (strcmp(pg->pg_name, name) == 0)
616255570Strasz			return (pg);
617255570Strasz	}
618255570Strasz
619255570Strasz	return (NULL);
620255570Strasz}
621255570Strasz
622255570Straszint
623255570Straszportal_group_add_listen(struct portal_group *pg, const char *value, bool iser)
624255570Strasz{
625255570Strasz	struct addrinfo hints;
626255570Strasz	struct portal *portal;
627255570Strasz	char *addr, *ch, *arg;
628255570Strasz	const char *port;
629255570Strasz	int error, colons = 0;
630255570Strasz
631255570Strasz	portal = portal_new(pg);
632255570Strasz	portal->p_listen = checked_strdup(value);
633255570Strasz	portal->p_iser = iser;
634255570Strasz
635255570Strasz	arg = portal->p_listen;
636255570Strasz	if (arg[0] == '\0') {
637255570Strasz		log_warnx("empty listen address");
638271632Strasz		portal_delete(portal);
639255570Strasz		return (1);
640255570Strasz	}
641255570Strasz	if (arg[0] == '[') {
642255570Strasz		/*
643255570Strasz		 * IPv6 address in square brackets, perhaps with port.
644255570Strasz		 */
645255570Strasz		arg++;
646255570Strasz		addr = strsep(&arg, "]");
647255570Strasz		if (arg == NULL) {
648255570Strasz			log_warnx("invalid listen address %s",
649255570Strasz			    portal->p_listen);
650271632Strasz			portal_delete(portal);
651255570Strasz			return (1);
652255570Strasz		}
653255570Strasz		if (arg[0] == '\0') {
654255570Strasz			port = "3260";
655255570Strasz		} else if (arg[0] == ':') {
656255570Strasz			port = arg + 1;
657255570Strasz		} else {
658255570Strasz			log_warnx("invalid listen address %s",
659255570Strasz			    portal->p_listen);
660271632Strasz			portal_delete(portal);
661255570Strasz			return (1);
662255570Strasz		}
663255570Strasz	} else {
664255570Strasz		/*
665255570Strasz		 * Either IPv6 address without brackets - and without
666255570Strasz		 * a port - or IPv4 address.  Just count the colons.
667255570Strasz		 */
668255570Strasz		for (ch = arg; *ch != '\0'; ch++) {
669255570Strasz			if (*ch == ':')
670255570Strasz				colons++;
671255570Strasz		}
672255570Strasz		if (colons > 1) {
673255570Strasz			addr = arg;
674255570Strasz			port = "3260";
675255570Strasz		} else {
676255570Strasz			addr = strsep(&arg, ":");
677255570Strasz			if (arg == NULL)
678255570Strasz				port = "3260";
679255570Strasz			else
680255570Strasz				port = arg;
681255570Strasz		}
682255570Strasz	}
683255570Strasz
684255570Strasz	memset(&hints, 0, sizeof(hints));
685255570Strasz	hints.ai_family = PF_UNSPEC;
686255570Strasz	hints.ai_socktype = SOCK_STREAM;
687255570Strasz	hints.ai_flags = AI_PASSIVE;
688255570Strasz
689255570Strasz	error = getaddrinfo(addr, port, &hints, &portal->p_ai);
690255570Strasz	if (error != 0) {
691255570Strasz		log_warnx("getaddrinfo for %s failed: %s",
692255570Strasz		    portal->p_listen, gai_strerror(error));
693271632Strasz		portal_delete(portal);
694255570Strasz		return (1);
695255570Strasz	}
696255570Strasz
697255570Strasz	/*
698255570Strasz	 * XXX: getaddrinfo(3) may return multiple addresses; we should turn
699255570Strasz	 *	those into multiple portals.
700255570Strasz	 */
701255570Strasz
702255570Strasz	return (0);
703255570Strasz}
704255570Strasz
705255570Straszstatic bool
706255570Straszvalid_hex(const char ch)
707255570Strasz{
708255570Strasz	switch (ch) {
709255570Strasz	case '0':
710255570Strasz	case '1':
711255570Strasz	case '2':
712255570Strasz	case '3':
713255570Strasz	case '4':
714255570Strasz	case '5':
715255570Strasz	case '6':
716255570Strasz	case '7':
717255570Strasz	case '8':
718255570Strasz	case '9':
719255570Strasz	case 'a':
720255570Strasz	case 'A':
721255570Strasz	case 'b':
722255570Strasz	case 'B':
723255570Strasz	case 'c':
724255570Strasz	case 'C':
725255570Strasz	case 'd':
726255570Strasz	case 'D':
727255570Strasz	case 'e':
728255570Strasz	case 'E':
729255570Strasz	case 'f':
730255570Strasz	case 'F':
731255570Strasz		return (true);
732255570Strasz	default:
733255570Strasz		return (false);
734255570Strasz	}
735255570Strasz}
736255570Strasz
737255570Straszbool
738255570Straszvalid_iscsi_name(const char *name)
739255570Strasz{
740255570Strasz	int i;
741255570Strasz
742255570Strasz	if (strlen(name) >= MAX_NAME_LEN) {
743255570Strasz		log_warnx("overlong name for target \"%s\"; max length allowed "
744255570Strasz		    "by iSCSI specification is %d characters",
745255570Strasz		    name, MAX_NAME_LEN);
746255570Strasz		return (false);
747255570Strasz	}
748255570Strasz
749255570Strasz	/*
750255570Strasz	 * In the cases below, we don't return an error, just in case the admin
751255570Strasz	 * was right, and we're wrong.
752255570Strasz	 */
753255570Strasz	if (strncasecmp(name, "iqn.", strlen("iqn.")) == 0) {
754255570Strasz		for (i = strlen("iqn."); name[i] != '\0'; i++) {
755255570Strasz			/*
756255570Strasz			 * XXX: We should verify UTF-8 normalisation, as defined
757255570Strasz			 * 	by 3.2.6.2: iSCSI Name Encoding.
758255570Strasz			 */
759255570Strasz			if (isalnum(name[i]))
760255570Strasz				continue;
761255570Strasz			if (name[i] == '-' || name[i] == '.' || name[i] == ':')
762255570Strasz				continue;
763255570Strasz			log_warnx("invalid character \"%c\" in target name "
764255570Strasz			    "\"%s\"; allowed characters are letters, digits, "
765255570Strasz			    "'-', '.', and ':'", name[i], name);
766255570Strasz			break;
767255570Strasz		}
768255570Strasz		/*
769255570Strasz		 * XXX: Check more stuff: valid date and a valid reversed domain.
770255570Strasz		 */
771255570Strasz	} else if (strncasecmp(name, "eui.", strlen("eui.")) == 0) {
772255570Strasz		if (strlen(name) != strlen("eui.") + 16)
773255570Strasz			log_warnx("invalid target name \"%s\"; the \"eui.\" "
774255570Strasz			    "should be followed by exactly 16 hexadecimal "
775255570Strasz			    "digits", name);
776255570Strasz		for (i = strlen("eui."); name[i] != '\0'; i++) {
777255570Strasz			if (!valid_hex(name[i])) {
778255570Strasz				log_warnx("invalid character \"%c\" in target "
779255570Strasz				    "name \"%s\"; allowed characters are 1-9 "
780255570Strasz				    "and A-F", name[i], name);
781255570Strasz				break;
782255570Strasz			}
783255570Strasz		}
784255570Strasz	} else if (strncasecmp(name, "naa.", strlen("naa.")) == 0) {
785255570Strasz		if (strlen(name) > strlen("naa.") + 32)
786255570Strasz			log_warnx("invalid target name \"%s\"; the \"naa.\" "
787255570Strasz			    "should be followed by at most 32 hexadecimal "
788255570Strasz			    "digits", name);
789255570Strasz		for (i = strlen("naa."); name[i] != '\0'; i++) {
790255570Strasz			if (!valid_hex(name[i])) {
791255570Strasz				log_warnx("invalid character \"%c\" in target "
792255570Strasz				    "name \"%s\"; allowed characters are 1-9 "
793255570Strasz				    "and A-F", name[i], name);
794255570Strasz				break;
795255570Strasz			}
796255570Strasz		}
797255570Strasz	} else {
798255570Strasz		log_warnx("invalid target name \"%s\"; should start with "
799255570Strasz		    "either \".iqn\", \"eui.\", or \"naa.\"",
800255570Strasz		    name);
801255570Strasz	}
802255570Strasz	return (true);
803255570Strasz}
804255570Strasz
805255570Straszstruct target *
806263723Strasztarget_new(struct conf *conf, const char *name)
807255570Strasz{
808255570Strasz	struct target *targ;
809255570Strasz	int i, len;
810255570Strasz
811263723Strasz	targ = target_find(conf, name);
812255570Strasz	if (targ != NULL) {
813263723Strasz		log_warnx("duplicated target \"%s\"", name);
814255570Strasz		return (NULL);
815255570Strasz	}
816263723Strasz	if (valid_iscsi_name(name) == false) {
817263723Strasz		log_warnx("target name \"%s\" is invalid", name);
818255570Strasz		return (NULL);
819255570Strasz	}
820255570Strasz	targ = calloc(1, sizeof(*targ));
821255570Strasz	if (targ == NULL)
822255570Strasz		log_err(1, "calloc");
823263723Strasz	targ->t_name = checked_strdup(name);
824255570Strasz
825255570Strasz	/*
826255570Strasz	 * RFC 3722 requires us to normalize the name to lowercase.
827255570Strasz	 */
828263723Strasz	len = strlen(name);
829255570Strasz	for (i = 0; i < len; i++)
830263723Strasz		targ->t_name[i] = tolower(targ->t_name[i]);
831255570Strasz
832255570Strasz	TAILQ_INIT(&targ->t_luns);
833255570Strasz	targ->t_conf = conf;
834255570Strasz	TAILQ_INSERT_TAIL(&conf->conf_targets, targ, t_next);
835255570Strasz
836255570Strasz	return (targ);
837255570Strasz}
838255570Strasz
839255570Straszvoid
840255570Strasztarget_delete(struct target *targ)
841255570Strasz{
842255570Strasz	struct lun *lun, *tmp;
843255570Strasz
844255570Strasz	TAILQ_REMOVE(&targ->t_conf->conf_targets, targ, t_next);
845255570Strasz
846255570Strasz	TAILQ_FOREACH_SAFE(lun, &targ->t_luns, l_next, tmp)
847255570Strasz		lun_delete(lun);
848263723Strasz	free(targ->t_name);
849255570Strasz	free(targ);
850255570Strasz}
851255570Strasz
852255570Straszstruct target *
853263723Strasztarget_find(struct conf *conf, const char *name)
854255570Strasz{
855255570Strasz	struct target *targ;
856255570Strasz
857255570Strasz	TAILQ_FOREACH(targ, &conf->conf_targets, t_next) {
858263723Strasz		if (strcasecmp(targ->t_name, name) == 0)
859255570Strasz			return (targ);
860255570Strasz	}
861255570Strasz
862255570Strasz	return (NULL);
863255570Strasz}
864255570Strasz
865255570Straszstruct lun *
866255570Straszlun_new(struct target *targ, int lun_id)
867255570Strasz{
868255570Strasz	struct lun *lun;
869255570Strasz
870255570Strasz	lun = lun_find(targ, lun_id);
871255570Strasz	if (lun != NULL) {
872255570Strasz		log_warnx("duplicated lun %d for target \"%s\"",
873263723Strasz		    lun_id, targ->t_name);
874255570Strasz		return (NULL);
875255570Strasz	}
876255570Strasz
877255570Strasz	lun = calloc(1, sizeof(*lun));
878255570Strasz	if (lun == NULL)
879255570Strasz		log_err(1, "calloc");
880255570Strasz	lun->l_lun = lun_id;
881255570Strasz	TAILQ_INIT(&lun->l_options);
882255570Strasz	lun->l_target = targ;
883255570Strasz	TAILQ_INSERT_TAIL(&targ->t_luns, lun, l_next);
884255570Strasz
885255570Strasz	return (lun);
886255570Strasz}
887255570Strasz
888255570Straszvoid
889255570Straszlun_delete(struct lun *lun)
890255570Strasz{
891255570Strasz	struct lun_option *lo, *tmp;
892255570Strasz
893255570Strasz	TAILQ_REMOVE(&lun->l_target->t_luns, lun, l_next);
894255570Strasz
895255570Strasz	TAILQ_FOREACH_SAFE(lo, &lun->l_options, lo_next, tmp)
896255570Strasz		lun_option_delete(lo);
897255570Strasz	free(lun->l_backend);
898255570Strasz	free(lun->l_device_id);
899255570Strasz	free(lun->l_path);
900255570Strasz	free(lun->l_serial);
901255570Strasz	free(lun);
902255570Strasz}
903255570Strasz
904255570Straszstruct lun *
905265514Straszlun_find(const struct target *targ, int lun_id)
906255570Strasz{
907255570Strasz	struct lun *lun;
908255570Strasz
909255570Strasz	TAILQ_FOREACH(lun, &targ->t_luns, l_next) {
910255570Strasz		if (lun->l_lun == lun_id)
911255570Strasz			return (lun);
912255570Strasz	}
913255570Strasz
914255570Strasz	return (NULL);
915255570Strasz}
916255570Strasz
917255570Straszvoid
918255570Straszlun_set_backend(struct lun *lun, const char *value)
919255570Strasz{
920255570Strasz	free(lun->l_backend);
921255570Strasz	lun->l_backend = checked_strdup(value);
922255570Strasz}
923255570Strasz
924255570Straszvoid
925255570Straszlun_set_blocksize(struct lun *lun, size_t value)
926255570Strasz{
927255570Strasz
928255570Strasz	lun->l_blocksize = value;
929255570Strasz}
930255570Strasz
931255570Straszvoid
932255570Straszlun_set_device_id(struct lun *lun, const char *value)
933255570Strasz{
934255570Strasz	free(lun->l_device_id);
935255570Strasz	lun->l_device_id = checked_strdup(value);
936255570Strasz}
937255570Strasz
938255570Straszvoid
939255570Straszlun_set_path(struct lun *lun, const char *value)
940255570Strasz{
941255570Strasz	free(lun->l_path);
942255570Strasz	lun->l_path = checked_strdup(value);
943255570Strasz}
944255570Strasz
945255570Straszvoid
946255570Straszlun_set_serial(struct lun *lun, const char *value)
947255570Strasz{
948255570Strasz	free(lun->l_serial);
949255570Strasz	lun->l_serial = checked_strdup(value);
950255570Strasz}
951255570Strasz
952255570Straszvoid
953255570Straszlun_set_size(struct lun *lun, size_t value)
954255570Strasz{
955255570Strasz
956255570Strasz	lun->l_size = value;
957255570Strasz}
958255570Strasz
959255570Straszvoid
960255570Straszlun_set_ctl_lun(struct lun *lun, uint32_t value)
961255570Strasz{
962255570Strasz
963255570Strasz	lun->l_ctl_lun = value;
964255570Strasz}
965255570Strasz
966255570Straszstruct lun_option *
967255570Straszlun_option_new(struct lun *lun, const char *name, const char *value)
968255570Strasz{
969255570Strasz	struct lun_option *lo;
970255570Strasz
971255570Strasz	lo = lun_option_find(lun, name);
972255570Strasz	if (lo != NULL) {
973255570Strasz		log_warnx("duplicated lun option %s for lun %d, target \"%s\"",
974263723Strasz		    name, lun->l_lun, lun->l_target->t_name);
975255570Strasz		return (NULL);
976255570Strasz	}
977255570Strasz
978255570Strasz	lo = calloc(1, sizeof(*lo));
979255570Strasz	if (lo == NULL)
980255570Strasz		log_err(1, "calloc");
981255570Strasz	lo->lo_name = checked_strdup(name);
982255570Strasz	lo->lo_value = checked_strdup(value);
983255570Strasz	lo->lo_lun = lun;
984255570Strasz	TAILQ_INSERT_TAIL(&lun->l_options, lo, lo_next);
985255570Strasz
986255570Strasz	return (lo);
987255570Strasz}
988255570Strasz
989255570Straszvoid
990255570Straszlun_option_delete(struct lun_option *lo)
991255570Strasz{
992255570Strasz
993255570Strasz	TAILQ_REMOVE(&lo->lo_lun->l_options, lo, lo_next);
994255570Strasz
995255570Strasz	free(lo->lo_name);
996255570Strasz	free(lo->lo_value);
997255570Strasz	free(lo);
998255570Strasz}
999255570Strasz
1000255570Straszstruct lun_option *
1001265514Straszlun_option_find(const struct lun *lun, const char *name)
1002255570Strasz{
1003255570Strasz	struct lun_option *lo;
1004255570Strasz
1005255570Strasz	TAILQ_FOREACH(lo, &lun->l_options, lo_next) {
1006255570Strasz		if (strcmp(lo->lo_name, name) == 0)
1007255570Strasz			return (lo);
1008255570Strasz	}
1009255570Strasz
1010255570Strasz	return (NULL);
1011255570Strasz}
1012255570Strasz
1013255570Straszvoid
1014255570Straszlun_option_set(struct lun_option *lo, const char *value)
1015255570Strasz{
1016255570Strasz
1017255570Strasz	free(lo->lo_value);
1018255570Strasz	lo->lo_value = checked_strdup(value);
1019255570Strasz}
1020255570Strasz
1021255570Straszstatic struct connection *
1022270137Smavconnection_new(struct portal *portal, int fd, const char *host,
1023270137Smav    const struct sockaddr *client_sa)
1024255570Strasz{
1025255570Strasz	struct connection *conn;
1026255570Strasz
1027255570Strasz	conn = calloc(1, sizeof(*conn));
1028255570Strasz	if (conn == NULL)
1029255570Strasz		log_err(1, "calloc");
1030255570Strasz	conn->conn_portal = portal;
1031255570Strasz	conn->conn_socket = fd;
1032255570Strasz	conn->conn_initiator_addr = checked_strdup(host);
1033270137Smav	memcpy(&conn->conn_initiator_sa, client_sa, client_sa->sa_len);
1034255570Strasz
1035255570Strasz	/*
1036255570Strasz	 * Default values, from RFC 3720, section 12.
1037255570Strasz	 */
1038255570Strasz	conn->conn_max_data_segment_length = 8192;
1039255570Strasz	conn->conn_max_burst_length = 262144;
1040255570Strasz	conn->conn_immediate_data = true;
1041255570Strasz
1042255570Strasz	return (conn);
1043255570Strasz}
1044255570Strasz
1045255570Strasz#if 0
1046255570Straszstatic void
1047255570Straszconf_print(struct conf *conf)
1048255570Strasz{
1049255570Strasz	struct auth_group *ag;
1050255570Strasz	struct auth *auth;
1051263720Strasz	struct auth_name *auth_name;
1052263720Strasz	struct auth_portal *auth_portal;
1053255570Strasz	struct portal_group *pg;
1054255570Strasz	struct portal *portal;
1055255570Strasz	struct target *targ;
1056255570Strasz	struct lun *lun;
1057255570Strasz	struct lun_option *lo;
1058255570Strasz
1059255570Strasz	TAILQ_FOREACH(ag, &conf->conf_auth_groups, ag_next) {
1060255570Strasz		fprintf(stderr, "auth-group %s {\n", ag->ag_name);
1061255570Strasz		TAILQ_FOREACH(auth, &ag->ag_auths, a_next)
1062255570Strasz			fprintf(stderr, "\t chap-mutual %s %s %s %s\n",
1063255570Strasz			    auth->a_user, auth->a_secret,
1064255570Strasz			    auth->a_mutual_user, auth->a_mutual_secret);
1065263720Strasz		TAILQ_FOREACH(auth_name, &ag->ag_names, an_next)
1066263720Strasz			fprintf(stderr, "\t initiator-name %s\n",
1067263720Strasz			    auth_name->an_initator_name);
1068263720Strasz		TAILQ_FOREACH(auth_portal, &ag->ag_portals, an_next)
1069263720Strasz			fprintf(stderr, "\t initiator-portal %s\n",
1070263720Strasz			    auth_portal->an_initator_portal);
1071255570Strasz		fprintf(stderr, "}\n");
1072255570Strasz	}
1073255570Strasz	TAILQ_FOREACH(pg, &conf->conf_portal_groups, pg_next) {
1074255570Strasz		fprintf(stderr, "portal-group %s {\n", pg->pg_name);
1075255570Strasz		TAILQ_FOREACH(portal, &pg->pg_portals, p_next)
1076255570Strasz			fprintf(stderr, "\t listen %s\n", portal->p_listen);
1077255570Strasz		fprintf(stderr, "}\n");
1078255570Strasz	}
1079255570Strasz	TAILQ_FOREACH(targ, &conf->conf_targets, t_next) {
1080263723Strasz		fprintf(stderr, "target %s {\n", targ->t_name);
1081255570Strasz		if (targ->t_alias != NULL)
1082255570Strasz			fprintf(stderr, "\t alias %s\n", targ->t_alias);
1083255570Strasz		TAILQ_FOREACH(lun, &targ->t_luns, l_next) {
1084255570Strasz			fprintf(stderr, "\tlun %d {\n", lun->l_lun);
1085255570Strasz			fprintf(stderr, "\t\tpath %s\n", lun->l_path);
1086255570Strasz			TAILQ_FOREACH(lo, &lun->l_options, lo_next)
1087255570Strasz				fprintf(stderr, "\t\toption %s %s\n",
1088255570Strasz				    lo->lo_name, lo->lo_value);
1089255570Strasz			fprintf(stderr, "\t}\n");
1090255570Strasz		}
1091255570Strasz		fprintf(stderr, "}\n");
1092255570Strasz	}
1093255570Strasz}
1094255570Strasz#endif
1095255570Strasz
1096263717Straszstatic int
1097263717Straszconf_verify_lun(struct lun *lun)
1098263717Strasz{
1099263717Strasz	const struct lun *lun2;
1100263718Strasz	const struct target *targ2;
1101263717Strasz
1102263717Strasz	if (lun->l_backend == NULL)
1103263717Strasz		lun_set_backend(lun, "block");
1104263717Strasz	if (strcmp(lun->l_backend, "block") == 0) {
1105263717Strasz		if (lun->l_path == NULL) {
1106263717Strasz			log_warnx("missing path for lun %d, target \"%s\"",
1107263723Strasz			    lun->l_lun, lun->l_target->t_name);
1108263717Strasz			return (1);
1109263717Strasz		}
1110263717Strasz	} else if (strcmp(lun->l_backend, "ramdisk") == 0) {
1111263717Strasz		if (lun->l_size == 0) {
1112263717Strasz			log_warnx("missing size for ramdisk-backed lun %d, "
1113263723Strasz			    "target \"%s\"", lun->l_lun, lun->l_target->t_name);
1114263717Strasz			return (1);
1115263717Strasz		}
1116263717Strasz		if (lun->l_path != NULL) {
1117263717Strasz			log_warnx("path must not be specified "
1118263717Strasz			    "for ramdisk-backed lun %d, target \"%s\"",
1119263723Strasz			    lun->l_lun, lun->l_target->t_name);
1120263717Strasz			return (1);
1121263717Strasz		}
1122263717Strasz	}
1123263717Strasz	if (lun->l_lun < 0 || lun->l_lun > 255) {
1124263717Strasz		log_warnx("invalid lun number for lun %d, target \"%s\"; "
1125263717Strasz		    "must be between 0 and 255", lun->l_lun,
1126263723Strasz		    lun->l_target->t_name);
1127263717Strasz		return (1);
1128263717Strasz	}
1129263717Strasz	if (lun->l_blocksize == 0) {
1130263717Strasz		lun_set_blocksize(lun, DEFAULT_BLOCKSIZE);
1131263717Strasz	} else if (lun->l_blocksize < 0) {
1132263717Strasz		log_warnx("invalid blocksize for lun %d, target \"%s\"; "
1133263723Strasz		    "must be larger than 0", lun->l_lun, lun->l_target->t_name);
1134263717Strasz		return (1);
1135263717Strasz	}
1136263717Strasz	if (lun->l_size != 0 && lun->l_size % lun->l_blocksize != 0) {
1137263717Strasz		log_warnx("invalid size for lun %d, target \"%s\"; "
1138263717Strasz		    "must be multiple of blocksize", lun->l_lun,
1139263723Strasz		    lun->l_target->t_name);
1140263717Strasz		return (1);
1141263717Strasz	}
1142263718Strasz	TAILQ_FOREACH(targ2, &lun->l_target->t_conf->conf_targets, t_next) {
1143263718Strasz		TAILQ_FOREACH(lun2, &targ2->t_luns, l_next) {
1144263718Strasz			if (lun == lun2)
1145263718Strasz				continue;
1146263718Strasz			if (lun->l_path != NULL && lun2->l_path != NULL &&
1147263718Strasz			    strcmp(lun->l_path, lun2->l_path) == 0) {
1148263718Strasz				log_debugx("WARNING: path \"%s\" duplicated "
1149263718Strasz				    "between lun %d, target \"%s\", and "
1150263718Strasz				    "lun %d, target \"%s\"", lun->l_path,
1151263723Strasz				    lun->l_lun, lun->l_target->t_name,
1152263723Strasz				    lun2->l_lun, lun2->l_target->t_name);
1153263718Strasz			}
1154263718Strasz		}
1155263718Strasz	}
1156263717Strasz
1157263717Strasz	return (0);
1158263717Strasz}
1159263717Strasz
1160255570Straszint
1161255570Straszconf_verify(struct conf *conf)
1162255570Strasz{
1163255570Strasz	struct auth_group *ag;
1164255570Strasz	struct portal_group *pg;
1165255570Strasz	struct target *targ;
1166263717Strasz	struct lun *lun;
1167265506Strasz	bool found_lun;
1168263717Strasz	int error;
1169255570Strasz
1170255570Strasz	if (conf->conf_pidfile_path == NULL)
1171255570Strasz		conf->conf_pidfile_path = checked_strdup(DEFAULT_PIDFILE);
1172255570Strasz
1173255570Strasz	TAILQ_FOREACH(targ, &conf->conf_targets, t_next) {
1174255570Strasz		if (targ->t_auth_group == NULL) {
1175263726Strasz			targ->t_auth_group = auth_group_find(conf,
1176263726Strasz			    "default");
1177263726Strasz			assert(targ->t_auth_group != NULL);
1178255570Strasz		}
1179255570Strasz		if (targ->t_portal_group == NULL) {
1180255570Strasz			targ->t_portal_group = portal_group_find(conf,
1181255570Strasz			    "default");
1182255570Strasz			assert(targ->t_portal_group != NULL);
1183255570Strasz		}
1184265506Strasz		found_lun = false;
1185255570Strasz		TAILQ_FOREACH(lun, &targ->t_luns, l_next) {
1186263717Strasz			error = conf_verify_lun(lun);
1187263717Strasz			if (error != 0)
1188263717Strasz				return (error);
1189265506Strasz			found_lun = true;
1190255570Strasz		}
1191265506Strasz		if (!found_lun) {
1192265506Strasz			log_warnx("no LUNs defined for target \"%s\"",
1193265506Strasz			    targ->t_name);
1194255570Strasz		}
1195255570Strasz	}
1196255570Strasz	TAILQ_FOREACH(pg, &conf->conf_portal_groups, pg_next) {
1197255570Strasz		assert(pg->pg_name != NULL);
1198255570Strasz		if (pg->pg_discovery_auth_group == NULL) {
1199255570Strasz			pg->pg_discovery_auth_group =
1200263728Strasz			    auth_group_find(conf, "default");
1201255570Strasz			assert(pg->pg_discovery_auth_group != NULL);
1202255570Strasz		}
1203255570Strasz
1204255570Strasz		TAILQ_FOREACH(targ, &conf->conf_targets, t_next) {
1205255570Strasz			if (targ->t_portal_group == pg)
1206255570Strasz				break;
1207255570Strasz		}
1208255570Strasz		if (targ == NULL) {
1209255570Strasz			if (strcmp(pg->pg_name, "default") != 0)
1210255570Strasz				log_warnx("portal-group \"%s\" not assigned "
1211255570Strasz				    "to any target", pg->pg_name);
1212255570Strasz			pg->pg_unassigned = true;
1213255570Strasz		} else
1214255570Strasz			pg->pg_unassigned = false;
1215255570Strasz	}
1216255570Strasz	TAILQ_FOREACH(ag, &conf->conf_auth_groups, ag_next) {
1217255570Strasz		if (ag->ag_name == NULL)
1218255570Strasz			assert(ag->ag_target != NULL);
1219255570Strasz		else
1220255570Strasz			assert(ag->ag_target == NULL);
1221255570Strasz
1222255570Strasz		TAILQ_FOREACH(targ, &conf->conf_targets, t_next) {
1223255570Strasz			if (targ->t_auth_group == ag)
1224255570Strasz				break;
1225255570Strasz		}
1226255570Strasz		if (targ == NULL && ag->ag_name != NULL &&
1227263728Strasz		    strcmp(ag->ag_name, "default") != 0 &&
1228255570Strasz		    strcmp(ag->ag_name, "no-authentication") != 0 &&
1229255570Strasz		    strcmp(ag->ag_name, "no-access") != 0) {
1230255570Strasz			log_warnx("auth-group \"%s\" not assigned "
1231255570Strasz			    "to any target", ag->ag_name);
1232255570Strasz		}
1233255570Strasz	}
1234255570Strasz
1235255570Strasz	return (0);
1236255570Strasz}
1237255570Strasz
1238255570Straszstatic int
1239255570Straszconf_apply(struct conf *oldconf, struct conf *newconf)
1240255570Strasz{
1241255570Strasz	struct target *oldtarg, *newtarg, *tmptarg;
1242255570Strasz	struct lun *oldlun, *newlun, *tmplun;
1243255570Strasz	struct portal_group *oldpg, *newpg;
1244255570Strasz	struct portal *oldp, *newp;
1245255570Strasz	pid_t otherpid;
1246255570Strasz	int changed, cumulated_error = 0, error;
1247255570Strasz	int one = 1;
1248255570Strasz
1249255570Strasz	if (oldconf->conf_debug != newconf->conf_debug) {
1250255570Strasz		log_debugx("changing debug level to %d", newconf->conf_debug);
1251255570Strasz		log_init(newconf->conf_debug);
1252255570Strasz	}
1253255570Strasz
1254255570Strasz	if (oldconf->conf_pidfh != NULL) {
1255255570Strasz		assert(oldconf->conf_pidfile_path != NULL);
1256255570Strasz		if (newconf->conf_pidfile_path != NULL &&
1257255570Strasz		    strcmp(oldconf->conf_pidfile_path,
1258255570Strasz		    newconf->conf_pidfile_path) == 0) {
1259255570Strasz			newconf->conf_pidfh = oldconf->conf_pidfh;
1260255570Strasz			oldconf->conf_pidfh = NULL;
1261255570Strasz		} else {
1262255570Strasz			log_debugx("removing pidfile %s",
1263255570Strasz			    oldconf->conf_pidfile_path);
1264255570Strasz			pidfile_remove(oldconf->conf_pidfh);
1265255570Strasz			oldconf->conf_pidfh = NULL;
1266255570Strasz		}
1267255570Strasz	}
1268255570Strasz
1269255570Strasz	if (newconf->conf_pidfh == NULL && newconf->conf_pidfile_path != NULL) {
1270255570Strasz		log_debugx("opening pidfile %s", newconf->conf_pidfile_path);
1271255570Strasz		newconf->conf_pidfh =
1272255570Strasz		    pidfile_open(newconf->conf_pidfile_path, 0600, &otherpid);
1273255570Strasz		if (newconf->conf_pidfh == NULL) {
1274255570Strasz			if (errno == EEXIST)
1275255570Strasz				log_errx(1, "daemon already running, pid: %jd.",
1276255570Strasz				    (intmax_t)otherpid);
1277255570Strasz			log_err(1, "cannot open or create pidfile \"%s\"",
1278255570Strasz			    newconf->conf_pidfile_path);
1279255570Strasz		}
1280255570Strasz	}
1281255570Strasz
1282265518Strasz	/*
1283265518Strasz	 * XXX: If target or lun removal fails, we should somehow "move"
1284265518Strasz	 * 	the old lun or target into newconf, so that subsequent
1285265519Strasz	 * 	conf_apply() would try to remove them again.  That would
1286265519Strasz	 * 	be somewhat hairy, though, and lun deletion failures don't
1287265519Strasz	 * 	really happen, so leave it as it is for now.
1288265518Strasz	 */
1289255570Strasz	TAILQ_FOREACH_SAFE(oldtarg, &oldconf->conf_targets, t_next, tmptarg) {
1290255570Strasz		/*
1291255570Strasz		 * First, remove any targets present in the old configuration
1292255570Strasz		 * and missing in the new one.
1293255570Strasz		 */
1294263723Strasz		newtarg = target_find(newconf, oldtarg->t_name);
1295255570Strasz		if (newtarg == NULL) {
1296255570Strasz			TAILQ_FOREACH_SAFE(oldlun, &oldtarg->t_luns, l_next,
1297255570Strasz			    tmplun) {
1298263716Strasz				log_debugx("target %s not found in new "
1299263716Strasz				    "configuration; removing its lun %d, "
1300255570Strasz				    "backed by CTL lun %d",
1301263723Strasz				    oldtarg->t_name, oldlun->l_lun,
1302255570Strasz				    oldlun->l_ctl_lun);
1303255570Strasz				error = kernel_lun_remove(oldlun);
1304255570Strasz				if (error != 0) {
1305255570Strasz					log_warnx("failed to remove lun %d, "
1306255570Strasz					    "target %s, CTL lun %d",
1307263723Strasz					    oldlun->l_lun, oldtarg->t_name,
1308255570Strasz					    oldlun->l_ctl_lun);
1309255570Strasz					cumulated_error++;
1310255570Strasz				}
1311255570Strasz				lun_delete(oldlun);
1312255570Strasz			}
1313268682Smav			kernel_port_remove(oldtarg);
1314255570Strasz			target_delete(oldtarg);
1315255570Strasz			continue;
1316255570Strasz		}
1317255570Strasz
1318255570Strasz		/*
1319255570Strasz		 * Second, remove any LUNs present in the old target
1320255570Strasz		 * and missing in the new one.
1321255570Strasz		 */
1322255570Strasz		TAILQ_FOREACH_SAFE(oldlun, &oldtarg->t_luns, l_next, tmplun) {
1323255570Strasz			newlun = lun_find(newtarg, oldlun->l_lun);
1324255570Strasz			if (newlun == NULL) {
1325255570Strasz				log_debugx("lun %d, target %s, CTL lun %d "
1326263716Strasz				    "not found in new configuration; "
1327263723Strasz				    "removing", oldlun->l_lun, oldtarg->t_name,
1328255570Strasz				    oldlun->l_ctl_lun);
1329255570Strasz				error = kernel_lun_remove(oldlun);
1330255570Strasz				if (error != 0) {
1331255570Strasz					log_warnx("failed to remove lun %d, "
1332255570Strasz					    "target %s, CTL lun %d",
1333263723Strasz					    oldlun->l_lun, oldtarg->t_name,
1334255570Strasz					    oldlun->l_ctl_lun);
1335255570Strasz					cumulated_error++;
1336255570Strasz				}
1337255570Strasz				lun_delete(oldlun);
1338255570Strasz				continue;
1339255570Strasz			}
1340255570Strasz
1341255570Strasz			/*
1342255570Strasz			 * Also remove the LUNs changed by more than size.
1343255570Strasz			 */
1344255570Strasz			changed = 0;
1345255570Strasz			assert(oldlun->l_backend != NULL);
1346255570Strasz			assert(newlun->l_backend != NULL);
1347255570Strasz			if (strcmp(newlun->l_backend, oldlun->l_backend) != 0) {
1348255570Strasz				log_debugx("backend for lun %d, target %s, "
1349255570Strasz				    "CTL lun %d changed; removing",
1350263723Strasz				    oldlun->l_lun, oldtarg->t_name,
1351255570Strasz				    oldlun->l_ctl_lun);
1352255570Strasz				changed = 1;
1353255570Strasz			}
1354255570Strasz			if (oldlun->l_blocksize != newlun->l_blocksize) {
1355255570Strasz				log_debugx("blocksize for lun %d, target %s, "
1356255570Strasz				    "CTL lun %d changed; removing",
1357263723Strasz				    oldlun->l_lun, oldtarg->t_name,
1358255570Strasz				    oldlun->l_ctl_lun);
1359255570Strasz				changed = 1;
1360255570Strasz			}
1361255570Strasz			if (newlun->l_device_id != NULL &&
1362255570Strasz			    (oldlun->l_device_id == NULL ||
1363255570Strasz			     strcmp(oldlun->l_device_id, newlun->l_device_id) !=
1364255570Strasz			     0)) {
1365255570Strasz				log_debugx("device-id for lun %d, target %s, "
1366255570Strasz				    "CTL lun %d changed; removing",
1367263723Strasz				    oldlun->l_lun, oldtarg->t_name,
1368255570Strasz				    oldlun->l_ctl_lun);
1369255570Strasz				changed = 1;
1370255570Strasz			}
1371255570Strasz			if (newlun->l_path != NULL &&
1372255570Strasz			    (oldlun->l_path == NULL ||
1373255570Strasz			     strcmp(oldlun->l_path, newlun->l_path) != 0)) {
1374255570Strasz				log_debugx("path for lun %d, target %s, "
1375255570Strasz				    "CTL lun %d, changed; removing",
1376263723Strasz				    oldlun->l_lun, oldtarg->t_name,
1377255570Strasz				    oldlun->l_ctl_lun);
1378255570Strasz				changed = 1;
1379255570Strasz			}
1380255570Strasz			if (newlun->l_serial != NULL &&
1381255570Strasz			    (oldlun->l_serial == NULL ||
1382255570Strasz			     strcmp(oldlun->l_serial, newlun->l_serial) != 0)) {
1383255570Strasz				log_debugx("serial for lun %d, target %s, "
1384255570Strasz				    "CTL lun %d changed; removing",
1385263723Strasz				    oldlun->l_lun, oldtarg->t_name,
1386255570Strasz				    oldlun->l_ctl_lun);
1387255570Strasz				changed = 1;
1388255570Strasz			}
1389255570Strasz			if (changed) {
1390255570Strasz				error = kernel_lun_remove(oldlun);
1391255570Strasz				if (error != 0) {
1392255570Strasz					log_warnx("failed to remove lun %d, "
1393255570Strasz					    "target %s, CTL lun %d",
1394263723Strasz					    oldlun->l_lun, oldtarg->t_name,
1395255570Strasz					    oldlun->l_ctl_lun);
1396255570Strasz					cumulated_error++;
1397255570Strasz				}
1398255570Strasz				lun_delete(oldlun);
1399255570Strasz				continue;
1400255570Strasz			}
1401255570Strasz
1402255570Strasz			lun_set_ctl_lun(newlun, oldlun->l_ctl_lun);
1403255570Strasz		}
1404255570Strasz	}
1405255570Strasz
1406255570Strasz	/*
1407255570Strasz	 * Now add new targets or modify existing ones.
1408255570Strasz	 */
1409255570Strasz	TAILQ_FOREACH(newtarg, &newconf->conf_targets, t_next) {
1410263723Strasz		oldtarg = target_find(oldconf, newtarg->t_name);
1411255570Strasz
1412265518Strasz		TAILQ_FOREACH_SAFE(newlun, &newtarg->t_luns, l_next, tmplun) {
1413255570Strasz			if (oldtarg != NULL) {
1414255570Strasz				oldlun = lun_find(oldtarg, newlun->l_lun);
1415255570Strasz				if (oldlun != NULL) {
1416271929Smav					if (newlun->l_size != oldlun->l_size ||
1417271929Smav					    newlun->l_size == 0) {
1418255570Strasz						log_debugx("resizing lun %d, "
1419255570Strasz						    "target %s, CTL lun %d",
1420255570Strasz						    newlun->l_lun,
1421263723Strasz						    newtarg->t_name,
1422255570Strasz						    newlun->l_ctl_lun);
1423255570Strasz						error =
1424255570Strasz						    kernel_lun_resize(newlun);
1425255570Strasz						if (error != 0) {
1426255570Strasz							log_warnx("failed to "
1427255570Strasz							    "resize lun %d, "
1428255570Strasz							    "target %s, "
1429255570Strasz							    "CTL lun %d",
1430255570Strasz							    newlun->l_lun,
1431263723Strasz							    newtarg->t_name,
1432255570Strasz							    newlun->l_lun);
1433255570Strasz							cumulated_error++;
1434255570Strasz						}
1435255570Strasz					}
1436255570Strasz					continue;
1437255570Strasz				}
1438255570Strasz			}
1439255570Strasz			log_debugx("adding lun %d, target %s",
1440263723Strasz			    newlun->l_lun, newtarg->t_name);
1441255570Strasz			error = kernel_lun_add(newlun);
1442255570Strasz			if (error != 0) {
1443255570Strasz				log_warnx("failed to add lun %d, target %s",
1444263723Strasz				    newlun->l_lun, newtarg->t_name);
1445265518Strasz				lun_delete(newlun);
1446255570Strasz				cumulated_error++;
1447255570Strasz			}
1448255570Strasz		}
1449268682Smav		if (oldtarg == NULL)
1450268682Smav			kernel_port_add(newtarg);
1451255570Strasz	}
1452255570Strasz
1453255570Strasz	/*
1454255570Strasz	 * Go through the new portals, opening the sockets as neccessary.
1455255570Strasz	 */
1456255570Strasz	TAILQ_FOREACH(newpg, &newconf->conf_portal_groups, pg_next) {
1457255570Strasz		if (newpg->pg_unassigned) {
1458255570Strasz			log_debugx("not listening on portal-group \"%s\", "
1459255570Strasz			    "not assigned to any target",
1460255570Strasz			    newpg->pg_name);
1461255570Strasz			continue;
1462255570Strasz		}
1463255570Strasz		TAILQ_FOREACH(newp, &newpg->pg_portals, p_next) {
1464255570Strasz			/*
1465255570Strasz			 * Try to find already open portal and reuse
1466255570Strasz			 * the listening socket.  We don't care about
1467255570Strasz			 * what portal or portal group that was, what
1468255570Strasz			 * matters is the listening address.
1469255570Strasz			 */
1470255570Strasz			TAILQ_FOREACH(oldpg, &oldconf->conf_portal_groups,
1471255570Strasz			    pg_next) {
1472255570Strasz				TAILQ_FOREACH(oldp, &oldpg->pg_portals,
1473255570Strasz				    p_next) {
1474255570Strasz					if (strcmp(newp->p_listen,
1475255570Strasz					    oldp->p_listen) == 0 &&
1476255570Strasz					    oldp->p_socket > 0) {
1477255570Strasz						newp->p_socket =
1478255570Strasz						    oldp->p_socket;
1479255570Strasz						oldp->p_socket = 0;
1480255570Strasz						break;
1481255570Strasz					}
1482255570Strasz				}
1483255570Strasz			}
1484255570Strasz			if (newp->p_socket > 0) {
1485255570Strasz				/*
1486255570Strasz				 * We're done with this portal.
1487255570Strasz				 */
1488255570Strasz				continue;
1489255570Strasz			}
1490255570Strasz
1491255570Strasz#ifdef ICL_KERNEL_PROXY
1492265507Strasz			if (proxy_mode) {
1493265509Strasz				newpg->pg_conf->conf_portal_id++;
1494265509Strasz				newp->p_id = newpg->pg_conf->conf_portal_id;
1495265509Strasz				log_debugx("listening on %s, portal-group "
1496265509Strasz				    "\"%s\", portal id %d, using ICL proxy",
1497265509Strasz				    newp->p_listen, newpg->pg_name, newp->p_id);
1498265509Strasz				kernel_listen(newp->p_ai, newp->p_iser,
1499265509Strasz				    newp->p_id);
1500265507Strasz				continue;
1501265507Strasz			}
1502265507Strasz#endif
1503265507Strasz			assert(proxy_mode == false);
1504255570Strasz			assert(newp->p_iser == false);
1505255570Strasz
1506255570Strasz			log_debugx("listening on %s, portal-group \"%s\"",
1507255570Strasz			    newp->p_listen, newpg->pg_name);
1508255570Strasz			newp->p_socket = socket(newp->p_ai->ai_family,
1509255570Strasz			    newp->p_ai->ai_socktype,
1510255570Strasz			    newp->p_ai->ai_protocol);
1511255570Strasz			if (newp->p_socket < 0) {
1512255570Strasz				log_warn("socket(2) failed for %s",
1513255570Strasz				    newp->p_listen);
1514255570Strasz				cumulated_error++;
1515255570Strasz				continue;
1516255570Strasz			}
1517255570Strasz			error = setsockopt(newp->p_socket, SOL_SOCKET,
1518255570Strasz			    SO_REUSEADDR, &one, sizeof(one));
1519255570Strasz			if (error != 0) {
1520255570Strasz				log_warn("setsockopt(SO_REUSEADDR) failed "
1521255570Strasz				    "for %s", newp->p_listen);
1522255570Strasz				close(newp->p_socket);
1523255570Strasz				newp->p_socket = 0;
1524255570Strasz				cumulated_error++;
1525255570Strasz				continue;
1526255570Strasz			}
1527255570Strasz			error = bind(newp->p_socket, newp->p_ai->ai_addr,
1528255570Strasz			    newp->p_ai->ai_addrlen);
1529255570Strasz			if (error != 0) {
1530255570Strasz				log_warn("bind(2) failed for %s",
1531255570Strasz				    newp->p_listen);
1532255570Strasz				close(newp->p_socket);
1533255570Strasz				newp->p_socket = 0;
1534255570Strasz				cumulated_error++;
1535255570Strasz				continue;
1536255570Strasz			}
1537255570Strasz			error = listen(newp->p_socket, -1);
1538255570Strasz			if (error != 0) {
1539255570Strasz				log_warn("listen(2) failed for %s",
1540255570Strasz				    newp->p_listen);
1541255570Strasz				close(newp->p_socket);
1542255570Strasz				newp->p_socket = 0;
1543255570Strasz				cumulated_error++;
1544255570Strasz				continue;
1545255570Strasz			}
1546255570Strasz		}
1547255570Strasz	}
1548255570Strasz
1549255570Strasz	/*
1550255570Strasz	 * Go through the no longer used sockets, closing them.
1551255570Strasz	 */
1552255570Strasz	TAILQ_FOREACH(oldpg, &oldconf->conf_portal_groups, pg_next) {
1553255570Strasz		TAILQ_FOREACH(oldp, &oldpg->pg_portals, p_next) {
1554255570Strasz			if (oldp->p_socket <= 0)
1555255570Strasz				continue;
1556255570Strasz			log_debugx("closing socket for %s, portal-group \"%s\"",
1557255570Strasz			    oldp->p_listen, oldpg->pg_name);
1558255570Strasz			close(oldp->p_socket);
1559255570Strasz			oldp->p_socket = 0;
1560255570Strasz		}
1561255570Strasz	}
1562255570Strasz
1563255570Strasz	return (cumulated_error);
1564255570Strasz}
1565255570Strasz
1566255570Straszbool
1567255570Strasztimed_out(void)
1568255570Strasz{
1569255570Strasz
1570255570Strasz	return (sigalrm_received);
1571255570Strasz}
1572255570Strasz
1573255570Straszstatic void
1574255570Straszsigalrm_handler(int dummy __unused)
1575255570Strasz{
1576255570Strasz	/*
1577255570Strasz	 * It would be easiest to just log an error and exit.  We can't
1578255570Strasz	 * do this, though, because log_errx() is not signal safe, since
1579255570Strasz	 * it calls syslog(3).  Instead, set a flag checked by pdu_send()
1580255570Strasz	 * and pdu_receive(), to call log_errx() there.  Should they fail
1581255570Strasz	 * to notice, we'll exit here one second later.
1582255570Strasz	 */
1583255570Strasz	if (sigalrm_received) {
1584255570Strasz		/*
1585255570Strasz		 * Oh well.  Just give up and quit.
1586255570Strasz		 */
1587255570Strasz		_exit(2);
1588255570Strasz	}
1589255570Strasz
1590255570Strasz	sigalrm_received = true;
1591255570Strasz}
1592255570Strasz
1593255570Straszstatic void
1594255570Straszset_timeout(const struct conf *conf)
1595255570Strasz{
1596255570Strasz	struct sigaction sa;
1597255570Strasz	struct itimerval itv;
1598255570Strasz	int error;
1599255570Strasz
1600255570Strasz	if (conf->conf_timeout <= 0) {
1601255570Strasz		log_debugx("session timeout disabled");
1602255570Strasz		return;
1603255570Strasz	}
1604255570Strasz
1605255570Strasz	bzero(&sa, sizeof(sa));
1606255570Strasz	sa.sa_handler = sigalrm_handler;
1607255570Strasz	sigfillset(&sa.sa_mask);
1608255570Strasz	error = sigaction(SIGALRM, &sa, NULL);
1609255570Strasz	if (error != 0)
1610255570Strasz		log_err(1, "sigaction");
1611255570Strasz
1612255570Strasz	/*
1613255570Strasz	 * First SIGALRM will arive after conf_timeout seconds.
1614255570Strasz	 * If we do nothing, another one will arrive a second later.
1615255570Strasz	 */
1616255570Strasz	bzero(&itv, sizeof(itv));
1617255570Strasz	itv.it_interval.tv_sec = 1;
1618255570Strasz	itv.it_value.tv_sec = conf->conf_timeout;
1619255570Strasz
1620255570Strasz	log_debugx("setting session timeout to %d seconds",
1621255570Strasz	    conf->conf_timeout);
1622255570Strasz	error = setitimer(ITIMER_REAL, &itv, NULL);
1623255570Strasz	if (error != 0)
1624255570Strasz		log_err(1, "setitimer");
1625255570Strasz}
1626255570Strasz
1627255570Straszstatic int
1628255570Straszwait_for_children(bool block)
1629255570Strasz{
1630255570Strasz	pid_t pid;
1631255570Strasz	int status;
1632255570Strasz	int num = 0;
1633255570Strasz
1634255570Strasz	for (;;) {
1635255570Strasz		/*
1636255570Strasz		 * If "block" is true, wait for at least one process.
1637255570Strasz		 */
1638255570Strasz		if (block && num == 0)
1639255570Strasz			pid = wait4(-1, &status, 0, NULL);
1640255570Strasz		else
1641255570Strasz			pid = wait4(-1, &status, WNOHANG, NULL);
1642255570Strasz		if (pid <= 0)
1643255570Strasz			break;
1644255570Strasz		if (WIFSIGNALED(status)) {
1645255570Strasz			log_warnx("child process %d terminated with signal %d",
1646255570Strasz			    pid, WTERMSIG(status));
1647255570Strasz		} else if (WEXITSTATUS(status) != 0) {
1648255570Strasz			log_warnx("child process %d terminated with exit status %d",
1649255570Strasz			    pid, WEXITSTATUS(status));
1650255570Strasz		} else {
1651255570Strasz			log_debugx("child process %d terminated gracefully", pid);
1652255570Strasz		}
1653255570Strasz		num++;
1654255570Strasz	}
1655255570Strasz
1656255570Strasz	return (num);
1657255570Strasz}
1658255570Strasz
1659255570Straszstatic void
1660265513Straszhandle_connection(struct portal *portal, int fd,
1661270137Smav    const struct sockaddr *client_sa, bool dont_fork)
1662255570Strasz{
1663255570Strasz	struct connection *conn;
1664255570Strasz	int error;
1665255570Strasz	pid_t pid;
1666255570Strasz	char host[NI_MAXHOST + 1];
1667255570Strasz	struct conf *conf;
1668255570Strasz
1669255570Strasz	conf = portal->p_portal_group->pg_conf;
1670255570Strasz
1671255570Strasz	if (dont_fork) {
1672255570Strasz		log_debugx("incoming connection; not forking due to -d flag");
1673255570Strasz	} else {
1674255570Strasz		nchildren -= wait_for_children(false);
1675255570Strasz		assert(nchildren >= 0);
1676255570Strasz
1677255570Strasz		while (conf->conf_maxproc > 0 && nchildren >= conf->conf_maxproc) {
1678255570Strasz			log_debugx("maxproc limit of %d child processes hit; "
1679255570Strasz			    "waiting for child process to exit", conf->conf_maxproc);
1680255570Strasz			nchildren -= wait_for_children(true);
1681255570Strasz			assert(nchildren >= 0);
1682255570Strasz		}
1683255570Strasz		log_debugx("incoming connection; forking child process #%d",
1684255570Strasz		    nchildren);
1685255570Strasz		nchildren++;
1686255570Strasz		pid = fork();
1687255570Strasz		if (pid < 0)
1688255570Strasz			log_err(1, "fork");
1689255570Strasz		if (pid > 0) {
1690255570Strasz			close(fd);
1691255570Strasz			return;
1692255570Strasz		}
1693255570Strasz	}
1694255570Strasz	pidfile_close(conf->conf_pidfh);
1695255570Strasz
1696270137Smav	error = getnameinfo(client_sa, client_sa->sa_len,
1697265513Strasz	    host, sizeof(host), NULL, 0, NI_NUMERICHOST);
1698265513Strasz	if (error != 0)
1699265513Strasz		log_errx(1, "getnameinfo: %s", gai_strerror(error));
1700255570Strasz
1701265513Strasz	log_debugx("accepted connection from %s; portal group \"%s\"",
1702265513Strasz	    host, portal->p_portal_group->pg_name);
1703265513Strasz	log_set_peer_addr(host);
1704265513Strasz	setproctitle("%s", host);
1705255570Strasz
1706270137Smav	conn = connection_new(portal, fd, host, client_sa);
1707255570Strasz	set_timeout(conf);
1708255570Strasz	kernel_capsicate();
1709255570Strasz	login(conn);
1710255570Strasz	if (conn->conn_session_type == CONN_SESSION_TYPE_NORMAL) {
1711255570Strasz		kernel_handoff(conn);
1712255570Strasz		log_debugx("connection handed off to the kernel");
1713255570Strasz	} else {
1714255570Strasz		assert(conn->conn_session_type == CONN_SESSION_TYPE_DISCOVERY);
1715255570Strasz		discovery(conn);
1716255570Strasz	}
1717255570Strasz	log_debugx("nothing more to do; exiting");
1718255570Strasz	exit(0);
1719255570Strasz}
1720255570Strasz
1721255570Straszstatic int
1722255570Straszfd_add(int fd, fd_set *fdset, int nfds)
1723255570Strasz{
1724255570Strasz
1725255570Strasz	/*
1726255570Strasz	 * Skip sockets which we failed to bind.
1727255570Strasz	 */
1728255570Strasz	if (fd <= 0)
1729255570Strasz		return (nfds);
1730255570Strasz
1731255570Strasz	FD_SET(fd, fdset);
1732255570Strasz	if (fd > nfds)
1733255570Strasz		nfds = fd;
1734255570Strasz	return (nfds);
1735255570Strasz}
1736255570Strasz
1737255570Straszstatic void
1738255570Straszmain_loop(struct conf *conf, bool dont_fork)
1739255570Strasz{
1740255570Strasz	struct portal_group *pg;
1741255570Strasz	struct portal *portal;
1742265512Strasz	struct sockaddr_storage client_sa;
1743265512Strasz	socklen_t client_salen;
1744255570Strasz#ifdef ICL_KERNEL_PROXY
1745255570Strasz	int connection_id;
1746265509Strasz	int portal_id;
1747265507Strasz#endif
1748255570Strasz	fd_set fdset;
1749255570Strasz	int error, nfds, client_fd;
1750255570Strasz
1751255570Strasz	pidfile_write(conf->conf_pidfh);
1752255570Strasz
1753255570Strasz	for (;;) {
1754255570Strasz		if (sighup_received || sigterm_received)
1755255570Strasz			return;
1756255570Strasz
1757255570Strasz#ifdef ICL_KERNEL_PROXY
1758265507Strasz		if (proxy_mode) {
1759265513Strasz			client_salen = sizeof(client_sa);
1760265513Strasz			kernel_accept(&connection_id, &portal_id,
1761265513Strasz			    (struct sockaddr *)&client_sa, &client_salen);
1762271627Strasz			assert(client_salen >= client_sa.ss_len);
1763255570Strasz
1764265509Strasz			log_debugx("incoming connection, id %d, portal id %d",
1765265509Strasz			    connection_id, portal_id);
1766265509Strasz			TAILQ_FOREACH(pg, &conf->conf_portal_groups, pg_next) {
1767265509Strasz				TAILQ_FOREACH(portal, &pg->pg_portals, p_next) {
1768265509Strasz					if (portal->p_id == portal_id) {
1769265509Strasz						goto found;
1770265509Strasz					}
1771265509Strasz				}
1772265509Strasz			}
1773255570Strasz
1774265509Strasz			log_errx(1, "kernel returned invalid portal_id %d",
1775265509Strasz			    portal_id);
1776265509Strasz
1777265509Straszfound:
1778265513Strasz			handle_connection(portal, connection_id,
1779270137Smav			    (struct sockaddr *)&client_sa, dont_fork);
1780265507Strasz		} else {
1781265507Strasz#endif
1782265507Strasz			assert(proxy_mode == false);
1783265507Strasz
1784265507Strasz			FD_ZERO(&fdset);
1785265507Strasz			nfds = 0;
1786265507Strasz			TAILQ_FOREACH(pg, &conf->conf_portal_groups, pg_next) {
1787265507Strasz				TAILQ_FOREACH(portal, &pg->pg_portals, p_next)
1788265507Strasz					nfds = fd_add(portal->p_socket, &fdset, nfds);
1789255570Strasz			}
1790265507Strasz			error = select(nfds + 1, &fdset, NULL, NULL, NULL);
1791265507Strasz			if (error <= 0) {
1792265507Strasz				if (errno == EINTR)
1793265507Strasz					return;
1794265507Strasz				log_err(1, "select");
1795265507Strasz			}
1796265507Strasz			TAILQ_FOREACH(pg, &conf->conf_portal_groups, pg_next) {
1797265507Strasz				TAILQ_FOREACH(portal, &pg->pg_portals, p_next) {
1798265507Strasz					if (!FD_ISSET(portal->p_socket, &fdset))
1799265507Strasz						continue;
1800265512Strasz					client_salen = sizeof(client_sa);
1801265512Strasz					client_fd = accept(portal->p_socket,
1802265512Strasz					    (struct sockaddr *)&client_sa,
1803265512Strasz					    &client_salen);
1804265507Strasz					if (client_fd < 0)
1805265507Strasz						log_err(1, "accept");
1806271627Strasz					assert(client_salen >= client_sa.ss_len);
1807271627Strasz
1808265512Strasz					handle_connection(portal, client_fd,
1809265513Strasz					    (struct sockaddr *)&client_sa,
1810270137Smav					    dont_fork);
1811265507Strasz					break;
1812265507Strasz				}
1813265507Strasz			}
1814265507Strasz#ifdef ICL_KERNEL_PROXY
1815255570Strasz		}
1816265507Strasz#endif
1817255570Strasz	}
1818255570Strasz}
1819255570Strasz
1820255570Straszstatic void
1821255570Straszsighup_handler(int dummy __unused)
1822255570Strasz{
1823255570Strasz
1824255570Strasz	sighup_received = true;
1825255570Strasz}
1826255570Strasz
1827255570Straszstatic void
1828255570Straszsigterm_handler(int dummy __unused)
1829255570Strasz{
1830255570Strasz
1831255570Strasz	sigterm_received = true;
1832255570Strasz}
1833255570Strasz
1834255570Straszstatic void
1835263730Straszsigchld_handler(int dummy __unused)
1836263730Strasz{
1837263730Strasz
1838263730Strasz	/*
1839263730Strasz	 * The only purpose of this handler is to make SIGCHLD
1840263730Strasz	 * interrupt the ISCSIDWAIT ioctl(2), so we can call
1841263730Strasz	 * wait_for_children().
1842263730Strasz	 */
1843263730Strasz}
1844263730Strasz
1845263730Straszstatic void
1846255570Straszregister_signals(void)
1847255570Strasz{
1848255570Strasz	struct sigaction sa;
1849255570Strasz	int error;
1850255570Strasz
1851255570Strasz	bzero(&sa, sizeof(sa));
1852255570Strasz	sa.sa_handler = sighup_handler;
1853255570Strasz	sigfillset(&sa.sa_mask);
1854255570Strasz	error = sigaction(SIGHUP, &sa, NULL);
1855255570Strasz	if (error != 0)
1856255570Strasz		log_err(1, "sigaction");
1857255570Strasz
1858255570Strasz	sa.sa_handler = sigterm_handler;
1859255570Strasz	error = sigaction(SIGTERM, &sa, NULL);
1860255570Strasz	if (error != 0)
1861255570Strasz		log_err(1, "sigaction");
1862255570Strasz
1863255570Strasz	sa.sa_handler = sigterm_handler;
1864255570Strasz	error = sigaction(SIGINT, &sa, NULL);
1865255570Strasz	if (error != 0)
1866255570Strasz		log_err(1, "sigaction");
1867263730Strasz
1868263730Strasz	sa.sa_handler = sigchld_handler;
1869263730Strasz	error = sigaction(SIGCHLD, &sa, NULL);
1870263730Strasz	if (error != 0)
1871263730Strasz		log_err(1, "sigaction");
1872255570Strasz}
1873255570Strasz
1874255570Straszint
1875255570Straszmain(int argc, char **argv)
1876255570Strasz{
1877255570Strasz	struct conf *oldconf, *newconf, *tmpconf;
1878255570Strasz	const char *config_path = DEFAULT_CONFIG_PATH;
1879255570Strasz	int debug = 0, ch, error;
1880255570Strasz	bool dont_daemonize = false;
1881255570Strasz
1882265507Strasz	while ((ch = getopt(argc, argv, "df:R")) != -1) {
1883255570Strasz		switch (ch) {
1884255570Strasz		case 'd':
1885255570Strasz			dont_daemonize = true;
1886255570Strasz			debug++;
1887255570Strasz			break;
1888255570Strasz		case 'f':
1889255570Strasz			config_path = optarg;
1890255570Strasz			break;
1891265507Strasz		case 'R':
1892265507Strasz#ifndef ICL_KERNEL_PROXY
1893265507Strasz			log_errx(1, "ctld(8) compiled without ICL_KERNEL_PROXY "
1894265507Strasz			    "does not support iSER protocol");
1895265507Strasz#endif
1896265507Strasz			proxy_mode = true;
1897265507Strasz			break;
1898255570Strasz		case '?':
1899255570Strasz		default:
1900255570Strasz			usage();
1901255570Strasz		}
1902255570Strasz	}
1903255570Strasz	argc -= optind;
1904255570Strasz	if (argc != 0)
1905255570Strasz		usage();
1906255570Strasz
1907255570Strasz	log_init(debug);
1908255570Strasz	kernel_init();
1909255570Strasz
1910255570Strasz	oldconf = conf_new_from_kernel();
1911255570Strasz	newconf = conf_new_from_file(config_path);
1912255570Strasz	if (newconf == NULL)
1913265516Strasz		log_errx(1, "configuration error; exiting");
1914255570Strasz	if (debug > 0) {
1915255570Strasz		oldconf->conf_debug = debug;
1916255570Strasz		newconf->conf_debug = debug;
1917255570Strasz	}
1918255570Strasz
1919255570Strasz	error = conf_apply(oldconf, newconf);
1920255570Strasz	if (error != 0)
1921265516Strasz		log_errx(1, "failed to apply configuration; exiting");
1922265516Strasz
1923255570Strasz	conf_delete(oldconf);
1924255570Strasz	oldconf = NULL;
1925255570Strasz
1926255570Strasz	register_signals();
1927255570Strasz
1928263719Strasz	if (dont_daemonize == false) {
1929263719Strasz		log_debugx("daemonizing");
1930263719Strasz		if (daemon(0, 0) == -1) {
1931263719Strasz			log_warn("cannot daemonize");
1932263719Strasz			pidfile_remove(newconf->conf_pidfh);
1933263719Strasz			exit(1);
1934263719Strasz		}
1935263719Strasz	}
1936263719Strasz
1937255570Strasz	for (;;) {
1938255570Strasz		main_loop(newconf, dont_daemonize);
1939255570Strasz		if (sighup_received) {
1940255570Strasz			sighup_received = false;
1941255570Strasz			log_debugx("received SIGHUP, reloading configuration");
1942255570Strasz			tmpconf = conf_new_from_file(config_path);
1943255570Strasz			if (tmpconf == NULL) {
1944255570Strasz				log_warnx("configuration error, "
1945255570Strasz				    "continuing with old configuration");
1946255570Strasz			} else {
1947255570Strasz				if (debug > 0)
1948255570Strasz					tmpconf->conf_debug = debug;
1949255570Strasz				oldconf = newconf;
1950255570Strasz				newconf = tmpconf;
1951255570Strasz				error = conf_apply(oldconf, newconf);
1952255570Strasz				if (error != 0)
1953255570Strasz					log_warnx("failed to reload "
1954255570Strasz					    "configuration");
1955255570Strasz				conf_delete(oldconf);
1956255570Strasz				oldconf = NULL;
1957255570Strasz			}
1958255570Strasz		} else if (sigterm_received) {
1959255570Strasz			log_debugx("exiting on signal; "
1960255570Strasz			    "reloading empty configuration");
1961255570Strasz
1962255570Strasz			log_debugx("disabling CTL iSCSI port "
1963255570Strasz			    "and terminating all connections");
1964255570Strasz
1965255570Strasz			oldconf = newconf;
1966255570Strasz			newconf = conf_new();
1967255570Strasz			if (debug > 0)
1968255570Strasz				newconf->conf_debug = debug;
1969255570Strasz			error = conf_apply(oldconf, newconf);
1970255570Strasz			if (error != 0)
1971255570Strasz				log_warnx("failed to apply configuration");
1972255570Strasz
1973255570Strasz			log_warnx("exiting on signal");
1974255570Strasz			exit(0);
1975255570Strasz		} else {
1976255570Strasz			nchildren -= wait_for_children(false);
1977255570Strasz			assert(nchildren >= 0);
1978255570Strasz		}
1979255570Strasz	}
1980255570Strasz	/* NOTREACHED */
1981255570Strasz}
1982