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
31270279Strasz#include <sys/cdefs.h>
32270279Strasz__FBSDID("$FreeBSD: releng/11.0/usr.sbin/ctld/ctld.c 302663 2016-07-12 13:12:01Z trasz $");
33270279Strasz
34255570Strasz#include <sys/types.h>
35255570Strasz#include <sys/time.h>
36255570Strasz#include <sys/socket.h>
37295212Sjceel#include <sys/stat.h>
38255570Strasz#include <sys/wait.h>
39255570Strasz#include <netinet/in.h>
40269183Smav#include <arpa/inet.h>
41255570Strasz#include <assert.h>
42255570Strasz#include <ctype.h>
43255570Strasz#include <errno.h>
44255570Strasz#include <netdb.h>
45255570Strasz#include <signal.h>
46255570Strasz#include <stdbool.h>
47255570Strasz#include <stdio.h>
48255570Strasz#include <stdint.h>
49255570Strasz#include <stdlib.h>
50255570Strasz#include <string.h>
51255570Strasz#include <unistd.h>
52255570Strasz
53255570Strasz#include "ctld.h"
54273635Smav#include "isns.h"
55255570Strasz
56264524Straszbool proxy_mode = false;
57264524Strasz
58255570Straszstatic volatile bool sighup_received = false;
59255570Straszstatic volatile bool sigterm_received = false;
60255570Straszstatic volatile bool sigalrm_received = false;
61255570Strasz
62255570Straszstatic int nchildren = 0;
63287534Smavstatic uint16_t last_portal_group_tag = 0xff;
64255570Strasz
65255570Straszstatic void
66255570Straszusage(void)
67255570Strasz{
68255570Strasz
69297860Strasz	fprintf(stderr, "usage: ctld [-d][-u][-f config-file]\n");
70255570Strasz	exit(1);
71255570Strasz}
72255570Strasz
73255570Straszchar *
74255570Straszchecked_strdup(const char *s)
75255570Strasz{
76255570Strasz	char *c;
77255570Strasz
78255570Strasz	c = strdup(s);
79255570Strasz	if (c == NULL)
80255570Strasz		log_err(1, "strdup");
81255570Strasz	return (c);
82255570Strasz}
83255570Strasz
84255570Straszstruct conf *
85255570Straszconf_new(void)
86255570Strasz{
87255570Strasz	struct conf *conf;
88255570Strasz
89255570Strasz	conf = calloc(1, sizeof(*conf));
90255570Strasz	if (conf == NULL)
91255570Strasz		log_err(1, "calloc");
92278037Smav	TAILQ_INIT(&conf->conf_luns);
93255570Strasz	TAILQ_INIT(&conf->conf_targets);
94255570Strasz	TAILQ_INIT(&conf->conf_auth_groups);
95278322Smav	TAILQ_INIT(&conf->conf_ports);
96255570Strasz	TAILQ_INIT(&conf->conf_portal_groups);
97278354Smav	TAILQ_INIT(&conf->conf_pports);
98273635Smav	TAILQ_INIT(&conf->conf_isns);
99255570Strasz
100273635Smav	conf->conf_isns_period = 900;
101273635Smav	conf->conf_isns_timeout = 5;
102255570Strasz	conf->conf_debug = 0;
103255570Strasz	conf->conf_timeout = 60;
104255570Strasz	conf->conf_maxproc = 30;
105255570Strasz
106255570Strasz	return (conf);
107255570Strasz}
108255570Strasz
109255570Straszvoid
110255570Straszconf_delete(struct conf *conf)
111255570Strasz{
112278037Smav	struct lun *lun, *ltmp;
113255570Strasz	struct target *targ, *tmp;
114255570Strasz	struct auth_group *ag, *cagtmp;
115255570Strasz	struct portal_group *pg, *cpgtmp;
116278354Smav	struct pport *pp, *pptmp;
117273635Smav	struct isns *is, *istmp;
118255570Strasz
119255570Strasz	assert(conf->conf_pidfh == NULL);
120255570Strasz
121278037Smav	TAILQ_FOREACH_SAFE(lun, &conf->conf_luns, l_next, ltmp)
122278037Smav		lun_delete(lun);
123255570Strasz	TAILQ_FOREACH_SAFE(targ, &conf->conf_targets, t_next, tmp)
124255570Strasz		target_delete(targ);
125255570Strasz	TAILQ_FOREACH_SAFE(ag, &conf->conf_auth_groups, ag_next, cagtmp)
126255570Strasz		auth_group_delete(ag);
127255570Strasz	TAILQ_FOREACH_SAFE(pg, &conf->conf_portal_groups, pg_next, cpgtmp)
128255570Strasz		portal_group_delete(pg);
129278354Smav	TAILQ_FOREACH_SAFE(pp, &conf->conf_pports, pp_next, pptmp)
130278354Smav		pport_delete(pp);
131273635Smav	TAILQ_FOREACH_SAFE(is, &conf->conf_isns, i_next, istmp)
132273635Smav		isns_delete(is);
133278322Smav	assert(TAILQ_EMPTY(&conf->conf_ports));
134255570Strasz	free(conf->conf_pidfile_path);
135255570Strasz	free(conf);
136255570Strasz}
137255570Strasz
138255570Straszstatic struct auth *
139255570Straszauth_new(struct auth_group *ag)
140255570Strasz{
141255570Strasz	struct auth *auth;
142255570Strasz
143255570Strasz	auth = calloc(1, sizeof(*auth));
144255570Strasz	if (auth == NULL)
145255570Strasz		log_err(1, "calloc");
146255570Strasz	auth->a_auth_group = ag;
147255570Strasz	TAILQ_INSERT_TAIL(&ag->ag_auths, auth, a_next);
148255570Strasz	return (auth);
149255570Strasz}
150255570Strasz
151255570Straszstatic void
152255570Straszauth_delete(struct auth *auth)
153255570Strasz{
154255570Strasz	TAILQ_REMOVE(&auth->a_auth_group->ag_auths, auth, a_next);
155255570Strasz
156255570Strasz	free(auth->a_user);
157255570Strasz	free(auth->a_secret);
158255570Strasz	free(auth->a_mutual_user);
159255570Strasz	free(auth->a_mutual_secret);
160255570Strasz	free(auth);
161255570Strasz}
162255570Strasz
163255570Straszconst struct auth *
164264531Straszauth_find(const struct auth_group *ag, const char *user)
165255570Strasz{
166255570Strasz	const struct auth *auth;
167255570Strasz
168255570Strasz	TAILQ_FOREACH(auth, &ag->ag_auths, a_next) {
169255570Strasz		if (strcmp(auth->a_user, user) == 0)
170255570Strasz			return (auth);
171255570Strasz	}
172255570Strasz
173255570Strasz	return (NULL);
174255570Strasz}
175255570Strasz
176261755Straszstatic void
177261755Straszauth_check_secret_length(struct auth *auth)
178261755Strasz{
179261755Strasz	size_t len;
180261755Strasz
181261755Strasz	len = strlen(auth->a_secret);
182261755Strasz	if (len > 16) {
183261755Strasz		if (auth->a_auth_group->ag_name != NULL)
184261755Strasz			log_warnx("secret for user \"%s\", auth-group \"%s\", "
185261755Strasz			    "is too long; it should be at most 16 characters "
186261755Strasz			    "long", auth->a_user, auth->a_auth_group->ag_name);
187261755Strasz		else
188261755Strasz			log_warnx("secret for user \"%s\", target \"%s\", "
189261755Strasz			    "is too long; it should be at most 16 characters "
190261755Strasz			    "long", auth->a_user,
191261757Strasz			    auth->a_auth_group->ag_target->t_name);
192261755Strasz	}
193261755Strasz	if (len < 12) {
194261755Strasz		if (auth->a_auth_group->ag_name != NULL)
195261755Strasz			log_warnx("secret for user \"%s\", auth-group \"%s\", "
196261755Strasz			    "is too short; it should be at least 12 characters "
197261755Strasz			    "long", auth->a_user,
198261755Strasz			    auth->a_auth_group->ag_name);
199261755Strasz		else
200261755Strasz			log_warnx("secret for user \"%s\", target \"%s\", "
201261755Strasz			    "is too short; it should be at least 16 characters "
202261755Strasz			    "long", auth->a_user,
203261757Strasz			    auth->a_auth_group->ag_target->t_name);
204261755Strasz	}
205261755Strasz
206261755Strasz	if (auth->a_mutual_secret != NULL) {
207281084Sjpaetzel		len = strlen(auth->a_mutual_secret);
208261755Strasz		if (len > 16) {
209261755Strasz			if (auth->a_auth_group->ag_name != NULL)
210261755Strasz				log_warnx("mutual secret for user \"%s\", "
211261755Strasz				    "auth-group \"%s\", is too long; it should "
212261755Strasz				    "be at most 16 characters long",
213261755Strasz				    auth->a_user, auth->a_auth_group->ag_name);
214261755Strasz			else
215261755Strasz				log_warnx("mutual secret for user \"%s\", "
216261755Strasz				    "target \"%s\", is too long; it should "
217261755Strasz				    "be at most 16 characters long",
218261755Strasz				    auth->a_user,
219261757Strasz				    auth->a_auth_group->ag_target->t_name);
220261755Strasz		}
221261755Strasz		if (len < 12) {
222261755Strasz			if (auth->a_auth_group->ag_name != NULL)
223261755Strasz				log_warnx("mutual secret for user \"%s\", "
224261755Strasz				    "auth-group \"%s\", is too short; it "
225261755Strasz				    "should be at least 12 characters long",
226261755Strasz				    auth->a_user, auth->a_auth_group->ag_name);
227261755Strasz			else
228261755Strasz				log_warnx("mutual secret for user \"%s\", "
229261755Strasz				    "target \"%s\", is too short; it should be "
230261755Strasz				    "at least 16 characters long",
231261755Strasz				    auth->a_user,
232261757Strasz				    auth->a_auth_group->ag_target->t_name);
233261755Strasz		}
234261755Strasz	}
235261755Strasz}
236261755Strasz
237261755Straszconst struct auth *
238261755Straszauth_new_chap(struct auth_group *ag, const char *user,
239261755Strasz    const char *secret)
240261755Strasz{
241261755Strasz	struct auth *auth;
242261755Strasz
243261755Strasz	if (ag->ag_type == AG_TYPE_UNKNOWN)
244261755Strasz		ag->ag_type = AG_TYPE_CHAP;
245261755Strasz	if (ag->ag_type != AG_TYPE_CHAP) {
246261755Strasz		if (ag->ag_name != NULL)
247261755Strasz			log_warnx("cannot mix \"chap\" authentication with "
248261755Strasz			    "other types for auth-group \"%s\"", ag->ag_name);
249261755Strasz		else
250261755Strasz			log_warnx("cannot mix \"chap\" authentication with "
251261755Strasz			    "other types for target \"%s\"",
252261757Strasz			    ag->ag_target->t_name);
253261755Strasz		return (NULL);
254261755Strasz	}
255261755Strasz
256261755Strasz	auth = auth_new(ag);
257261755Strasz	auth->a_user = checked_strdup(user);
258261755Strasz	auth->a_secret = checked_strdup(secret);
259261755Strasz
260261755Strasz	auth_check_secret_length(auth);
261261755Strasz
262261755Strasz	return (auth);
263261755Strasz}
264261755Strasz
265261755Straszconst struct auth *
266261755Straszauth_new_chap_mutual(struct auth_group *ag, const char *user,
267261755Strasz    const char *secret, const char *user2, const char *secret2)
268261755Strasz{
269261755Strasz	struct auth *auth;
270261755Strasz
271261755Strasz	if (ag->ag_type == AG_TYPE_UNKNOWN)
272261755Strasz		ag->ag_type = AG_TYPE_CHAP_MUTUAL;
273261755Strasz	if (ag->ag_type != AG_TYPE_CHAP_MUTUAL) {
274261755Strasz		if (ag->ag_name != NULL)
275261755Strasz			log_warnx("cannot mix \"chap-mutual\" authentication "
276261755Strasz			    "with other types for auth-group \"%s\"",
277273464Strasz			    ag->ag_name);
278261755Strasz		else
279261755Strasz			log_warnx("cannot mix \"chap-mutual\" authentication "
280261755Strasz			    "with other types for target \"%s\"",
281261757Strasz			    ag->ag_target->t_name);
282261755Strasz		return (NULL);
283261755Strasz	}
284261755Strasz
285261755Strasz	auth = auth_new(ag);
286261755Strasz	auth->a_user = checked_strdup(user);
287261755Strasz	auth->a_secret = checked_strdup(secret);
288261755Strasz	auth->a_mutual_user = checked_strdup(user2);
289261755Strasz	auth->a_mutual_secret = checked_strdup(secret2);
290261755Strasz
291261755Strasz	auth_check_secret_length(auth);
292261755Strasz
293261755Strasz	return (auth);
294261755Strasz}
295261755Strasz
296261754Straszconst struct auth_name *
297261754Straszauth_name_new(struct auth_group *ag, const char *name)
298261754Strasz{
299261754Strasz	struct auth_name *an;
300261754Strasz
301261754Strasz	an = calloc(1, sizeof(*an));
302261754Strasz	if (an == NULL)
303261754Strasz		log_err(1, "calloc");
304261754Strasz	an->an_auth_group = ag;
305261754Strasz	an->an_initator_name = checked_strdup(name);
306261754Strasz	TAILQ_INSERT_TAIL(&ag->ag_names, an, an_next);
307261754Strasz	return (an);
308261754Strasz}
309261754Strasz
310261754Straszstatic void
311261754Straszauth_name_delete(struct auth_name *an)
312261754Strasz{
313261754Strasz	TAILQ_REMOVE(&an->an_auth_group->ag_names, an, an_next);
314261754Strasz
315261754Strasz	free(an->an_initator_name);
316261754Strasz	free(an);
317261754Strasz}
318261754Strasz
319261754Straszbool
320261754Straszauth_name_defined(const struct auth_group *ag)
321261754Strasz{
322261754Strasz	if (TAILQ_EMPTY(&ag->ag_names))
323261754Strasz		return (false);
324261754Strasz	return (true);
325261754Strasz}
326261754Strasz
327261754Straszconst struct auth_name *
328261754Straszauth_name_find(const struct auth_group *ag, const char *name)
329261754Strasz{
330261754Strasz	const struct auth_name *auth_name;
331261754Strasz
332261754Strasz	TAILQ_FOREACH(auth_name, &ag->ag_names, an_next) {
333261754Strasz		if (strcmp(auth_name->an_initator_name, name) == 0)
334261754Strasz			return (auth_name);
335261754Strasz	}
336261754Strasz
337261754Strasz	return (NULL);
338261754Strasz}
339261754Strasz
340273584Straszint
341273584Straszauth_name_check(const struct auth_group *ag, const char *initiator_name)
342273584Strasz{
343273584Strasz	if (!auth_name_defined(ag))
344273584Strasz		return (0);
345273584Strasz
346273584Strasz	if (auth_name_find(ag, initiator_name) == NULL)
347273584Strasz		return (1);
348273584Strasz
349273584Strasz	return (0);
350273584Strasz}
351273584Strasz
352261754Straszconst struct auth_portal *
353261754Straszauth_portal_new(struct auth_group *ag, const char *portal)
354261754Strasz{
355261754Strasz	struct auth_portal *ap;
356269183Smav	char *net, *mask, *str, *tmp;
357269183Smav	int len, dm, m;
358261754Strasz
359261754Strasz	ap = calloc(1, sizeof(*ap));
360261754Strasz	if (ap == NULL)
361261754Strasz		log_err(1, "calloc");
362261754Strasz	ap->ap_auth_group = ag;
363261754Strasz	ap->ap_initator_portal = checked_strdup(portal);
364269183Smav	mask = str = checked_strdup(portal);
365269183Smav	net = strsep(&mask, "/");
366269183Smav	if (net[0] == '[')
367269183Smav		net++;
368269183Smav	len = strlen(net);
369269183Smav	if (len == 0)
370269183Smav		goto error;
371269183Smav	if (net[len - 1] == ']')
372269183Smav		net[len - 1] = 0;
373269183Smav	if (strchr(net, ':') != NULL) {
374269183Smav		struct sockaddr_in6 *sin6 =
375269183Smav		    (struct sockaddr_in6 *)&ap->ap_sa;
376269183Smav
377269183Smav		sin6->sin6_len = sizeof(*sin6);
378269183Smav		sin6->sin6_family = AF_INET6;
379269183Smav		if (inet_pton(AF_INET6, net, &sin6->sin6_addr) <= 0)
380269183Smav			goto error;
381269183Smav		dm = 128;
382269183Smav	} else {
383269183Smav		struct sockaddr_in *sin =
384269183Smav		    (struct sockaddr_in *)&ap->ap_sa;
385269183Smav
386269183Smav		sin->sin_len = sizeof(*sin);
387269183Smav		sin->sin_family = AF_INET;
388269183Smav		if (inet_pton(AF_INET, net, &sin->sin_addr) <= 0)
389269183Smav			goto error;
390269183Smav		dm = 32;
391269183Smav	}
392269183Smav	if (mask != NULL) {
393269183Smav		m = strtol(mask, &tmp, 0);
394269183Smav		if (m < 0 || m > dm || tmp[0] != 0)
395269183Smav			goto error;
396269183Smav	} else
397269183Smav		m = dm;
398269183Smav	ap->ap_mask = m;
399269183Smav	free(str);
400261754Strasz	TAILQ_INSERT_TAIL(&ag->ag_portals, ap, ap_next);
401261754Strasz	return (ap);
402269183Smav
403269183Smaverror:
404279277Smav	free(ap);
405302663Strasz	log_warnx("incorrect initiator portal \"%s\"", portal);
406269183Smav	return (NULL);
407261754Strasz}
408261754Strasz
409261754Straszstatic void
410261754Straszauth_portal_delete(struct auth_portal *ap)
411261754Strasz{
412261754Strasz	TAILQ_REMOVE(&ap->ap_auth_group->ag_portals, ap, ap_next);
413261754Strasz
414261754Strasz	free(ap->ap_initator_portal);
415261754Strasz	free(ap);
416261754Strasz}
417261754Strasz
418261754Straszbool
419261754Straszauth_portal_defined(const struct auth_group *ag)
420261754Strasz{
421261754Strasz	if (TAILQ_EMPTY(&ag->ag_portals))
422261754Strasz		return (false);
423261754Strasz	return (true);
424261754Strasz}
425261754Strasz
426261754Straszconst struct auth_portal *
427269183Smavauth_portal_find(const struct auth_group *ag, const struct sockaddr_storage *ss)
428261754Strasz{
429269183Smav	const struct auth_portal *ap;
430269191Smav	const uint8_t *a, *b;
431269183Smav	int i;
432269191Smav	uint8_t bmask;
433261754Strasz
434269183Smav	TAILQ_FOREACH(ap, &ag->ag_portals, ap_next) {
435269183Smav		if (ap->ap_sa.ss_family != ss->ss_family)
436269183Smav			continue;
437269183Smav		if (ss->ss_family == AF_INET) {
438269191Smav			a = (const uint8_t *)
439269191Smav			    &((const struct sockaddr_in *)ss)->sin_addr;
440269191Smav			b = (const uint8_t *)
441269191Smav			    &((const struct sockaddr_in *)&ap->ap_sa)->sin_addr;
442269183Smav		} else {
443269191Smav			a = (const uint8_t *)
444269191Smav			    &((const struct sockaddr_in6 *)ss)->sin6_addr;
445269191Smav			b = (const uint8_t *)
446269191Smav			    &((const struct sockaddr_in6 *)&ap->ap_sa)->sin6_addr;
447269183Smav		}
448269183Smav		for (i = 0; i < ap->ap_mask / 8; i++) {
449269183Smav			if (a[i] != b[i])
450269183Smav				goto next;
451269183Smav		}
452269183Smav		if (ap->ap_mask % 8) {
453269183Smav			bmask = 0xff << (8 - (ap->ap_mask % 8));
454269183Smav			if ((a[i] & bmask) != (b[i] & bmask))
455269183Smav				goto next;
456269183Smav		}
457269183Smav		return (ap);
458269183Smavnext:
459269183Smav		;
460261754Strasz	}
461261754Strasz
462261754Strasz	return (NULL);
463261754Strasz}
464261754Strasz
465273584Straszint
466273584Straszauth_portal_check(const struct auth_group *ag, const struct sockaddr_storage *sa)
467273584Strasz{
468273584Strasz
469273584Strasz	if (!auth_portal_defined(ag))
470273584Strasz		return (0);
471273584Strasz
472273584Strasz	if (auth_portal_find(ag, sa) == NULL)
473273584Strasz		return (1);
474273584Strasz
475273584Strasz	return (0);
476273584Strasz}
477273584Strasz
478255570Straszstruct auth_group *
479255570Straszauth_group_new(struct conf *conf, const char *name)
480255570Strasz{
481255570Strasz	struct auth_group *ag;
482255570Strasz
483255570Strasz	if (name != NULL) {
484255570Strasz		ag = auth_group_find(conf, name);
485255570Strasz		if (ag != NULL) {
486255570Strasz			log_warnx("duplicated auth-group \"%s\"", name);
487255570Strasz			return (NULL);
488255570Strasz		}
489255570Strasz	}
490255570Strasz
491255570Strasz	ag = calloc(1, sizeof(*ag));
492255570Strasz	if (ag == NULL)
493255570Strasz		log_err(1, "calloc");
494255570Strasz	if (name != NULL)
495255570Strasz		ag->ag_name = checked_strdup(name);
496255570Strasz	TAILQ_INIT(&ag->ag_auths);
497261754Strasz	TAILQ_INIT(&ag->ag_names);
498261754Strasz	TAILQ_INIT(&ag->ag_portals);
499255570Strasz	ag->ag_conf = conf;
500255570Strasz	TAILQ_INSERT_TAIL(&conf->conf_auth_groups, ag, ag_next);
501255570Strasz
502255570Strasz	return (ag);
503255570Strasz}
504255570Strasz
505255570Straszvoid
506255570Straszauth_group_delete(struct auth_group *ag)
507255570Strasz{
508261754Strasz	struct auth *auth, *auth_tmp;
509261754Strasz	struct auth_name *auth_name, *auth_name_tmp;
510261754Strasz	struct auth_portal *auth_portal, *auth_portal_tmp;
511255570Strasz
512255570Strasz	TAILQ_REMOVE(&ag->ag_conf->conf_auth_groups, ag, ag_next);
513255570Strasz
514261754Strasz	TAILQ_FOREACH_SAFE(auth, &ag->ag_auths, a_next, auth_tmp)
515255570Strasz		auth_delete(auth);
516261754Strasz	TAILQ_FOREACH_SAFE(auth_name, &ag->ag_names, an_next, auth_name_tmp)
517261754Strasz		auth_name_delete(auth_name);
518261754Strasz	TAILQ_FOREACH_SAFE(auth_portal, &ag->ag_portals, ap_next,
519261754Strasz	    auth_portal_tmp)
520261754Strasz		auth_portal_delete(auth_portal);
521255570Strasz	free(ag->ag_name);
522255570Strasz	free(ag);
523255570Strasz}
524255570Strasz
525255570Straszstruct auth_group *
526264531Straszauth_group_find(const struct conf *conf, const char *name)
527255570Strasz{
528255570Strasz	struct auth_group *ag;
529255570Strasz
530255570Strasz	TAILQ_FOREACH(ag, &conf->conf_auth_groups, ag_next) {
531255570Strasz		if (ag->ag_name != NULL && strcmp(ag->ag_name, name) == 0)
532255570Strasz			return (ag);
533255570Strasz	}
534255570Strasz
535255570Strasz	return (NULL);
536255570Strasz}
537255570Strasz
538261758Straszint
539273816Straszauth_group_set_type(struct auth_group *ag, const char *str)
540261758Strasz{
541273816Strasz	int type;
542261758Strasz
543261758Strasz	if (strcmp(str, "none") == 0) {
544261758Strasz		type = AG_TYPE_NO_AUTHENTICATION;
545261763Strasz	} else if (strcmp(str, "deny") == 0) {
546261763Strasz		type = AG_TYPE_DENY;
547261758Strasz	} else if (strcmp(str, "chap") == 0) {
548261758Strasz		type = AG_TYPE_CHAP;
549261758Strasz	} else if (strcmp(str, "chap-mutual") == 0) {
550261758Strasz		type = AG_TYPE_CHAP_MUTUAL;
551261758Strasz	} else {
552261758Strasz		if (ag->ag_name != NULL)
553261758Strasz			log_warnx("invalid auth-type \"%s\" for auth-group "
554261758Strasz			    "\"%s\"", str, ag->ag_name);
555261758Strasz		else
556261758Strasz			log_warnx("invalid auth-type \"%s\" for target "
557261758Strasz			    "\"%s\"", str, ag->ag_target->t_name);
558261758Strasz		return (1);
559261758Strasz	}
560261758Strasz
561273816Strasz	if (ag->ag_type != AG_TYPE_UNKNOWN && ag->ag_type != type) {
562273816Strasz		if (ag->ag_name != NULL) {
563261758Strasz			log_warnx("cannot set auth-type to \"%s\" for "
564261758Strasz			    "auth-group \"%s\"; already has a different "
565261758Strasz			    "type", str, ag->ag_name);
566273816Strasz		} else {
567261758Strasz			log_warnx("cannot set auth-type to \"%s\" for target "
568261758Strasz			    "\"%s\"; already has a different type",
569261758Strasz			    str, ag->ag_target->t_name);
570273816Strasz		}
571261758Strasz		return (1);
572261758Strasz	}
573261758Strasz
574273816Strasz	ag->ag_type = type;
575273816Strasz
576273816Strasz	return (0);
577261758Strasz}
578261758Strasz
579255570Straszstatic struct portal *
580255570Straszportal_new(struct portal_group *pg)
581255570Strasz{
582255570Strasz	struct portal *portal;
583255570Strasz
584255570Strasz	portal = calloc(1, sizeof(*portal));
585255570Strasz	if (portal == NULL)
586255570Strasz		log_err(1, "calloc");
587255570Strasz	TAILQ_INIT(&portal->p_targets);
588255570Strasz	portal->p_portal_group = pg;
589255570Strasz	TAILQ_INSERT_TAIL(&pg->pg_portals, portal, p_next);
590255570Strasz	return (portal);
591255570Strasz}
592255570Strasz
593255570Straszstatic void
594255570Straszportal_delete(struct portal *portal)
595255570Strasz{
596271187Strasz
597255570Strasz	TAILQ_REMOVE(&portal->p_portal_group->pg_portals, portal, p_next);
598271187Strasz	if (portal->p_ai != NULL)
599271187Strasz		freeaddrinfo(portal->p_ai);
600255570Strasz	free(portal->p_listen);
601255570Strasz	free(portal);
602255570Strasz}
603255570Strasz
604255570Straszstruct portal_group *
605255570Straszportal_group_new(struct conf *conf, const char *name)
606255570Strasz{
607255570Strasz	struct portal_group *pg;
608255570Strasz
609255678Strasz	pg = portal_group_find(conf, name);
610255678Strasz	if (pg != NULL) {
611255678Strasz		log_warnx("duplicated portal-group \"%s\"", name);
612255678Strasz		return (NULL);
613255570Strasz	}
614255570Strasz
615255570Strasz	pg = calloc(1, sizeof(*pg));
616255570Strasz	if (pg == NULL)
617255570Strasz		log_err(1, "calloc");
618255570Strasz	pg->pg_name = checked_strdup(name);
619290615Smav	TAILQ_INIT(&pg->pg_options);
620255570Strasz	TAILQ_INIT(&pg->pg_portals);
621278322Smav	TAILQ_INIT(&pg->pg_ports);
622255570Strasz	pg->pg_conf = conf;
623278161Smav	pg->pg_tag = 0;		/* Assigned later in conf_apply(). */
624255570Strasz	TAILQ_INSERT_TAIL(&conf->conf_portal_groups, pg, pg_next);
625255570Strasz
626255570Strasz	return (pg);
627255570Strasz}
628255570Strasz
629255570Straszvoid
630255570Straszportal_group_delete(struct portal_group *pg)
631255570Strasz{
632255570Strasz	struct portal *portal, *tmp;
633278322Smav	struct port *port, *tport;
634290615Smav	struct option *o, *otmp;
635255570Strasz
636278322Smav	TAILQ_FOREACH_SAFE(port, &pg->pg_ports, p_pgs, tport)
637278322Smav		port_delete(port);
638255570Strasz	TAILQ_REMOVE(&pg->pg_conf->conf_portal_groups, pg, pg_next);
639255570Strasz
640255570Strasz	TAILQ_FOREACH_SAFE(portal, &pg->pg_portals, p_next, tmp)
641255570Strasz		portal_delete(portal);
642290615Smav	TAILQ_FOREACH_SAFE(o, &pg->pg_options, o_next, otmp)
643290615Smav		option_delete(&pg->pg_options, o);
644255570Strasz	free(pg->pg_name);
645279392Strasz	free(pg->pg_offload);
646274308Strasz	free(pg->pg_redirection);
647255570Strasz	free(pg);
648255570Strasz}
649255570Strasz
650255570Straszstruct portal_group *
651264531Straszportal_group_find(const struct conf *conf, const char *name)
652255570Strasz{
653255570Strasz	struct portal_group *pg;
654255570Strasz
655255570Strasz	TAILQ_FOREACH(pg, &conf->conf_portal_groups, pg_next) {
656255570Strasz		if (strcmp(pg->pg_name, name) == 0)
657255570Strasz			return (pg);
658255570Strasz	}
659255570Strasz
660255570Strasz	return (NULL);
661255570Strasz}
662255570Strasz
663273635Smavstatic int
664273635Smavparse_addr_port(char *arg, const char *def_port, struct addrinfo **ai)
665255570Strasz{
666255570Strasz	struct addrinfo hints;
667275452Smav	char *str, *addr, *ch;
668255570Strasz	const char *port;
669255570Strasz	int error, colons = 0;
670255570Strasz
671275452Smav	str = arg = strdup(arg);
672255570Strasz	if (arg[0] == '[') {
673255570Strasz		/*
674255570Strasz		 * IPv6 address in square brackets, perhaps with port.
675255570Strasz		 */
676255570Strasz		arg++;
677255570Strasz		addr = strsep(&arg, "]");
678273635Smav		if (arg == NULL)
679255570Strasz			return (1);
680255570Strasz		if (arg[0] == '\0') {
681273635Smav			port = def_port;
682255570Strasz		} else if (arg[0] == ':') {
683255570Strasz			port = arg + 1;
684275452Smav		} else {
685275452Smav			free(str);
686255570Strasz			return (1);
687275452Smav		}
688255570Strasz	} else {
689255570Strasz		/*
690255570Strasz		 * Either IPv6 address without brackets - and without
691255570Strasz		 * a port - or IPv4 address.  Just count the colons.
692255570Strasz		 */
693255570Strasz		for (ch = arg; *ch != '\0'; ch++) {
694255570Strasz			if (*ch == ':')
695255570Strasz				colons++;
696255570Strasz		}
697255570Strasz		if (colons > 1) {
698255570Strasz			addr = arg;
699273635Smav			port = def_port;
700255570Strasz		} else {
701255570Strasz			addr = strsep(&arg, ":");
702255570Strasz			if (arg == NULL)
703273635Smav				port = def_port;
704255570Strasz			else
705255570Strasz				port = arg;
706255570Strasz		}
707255570Strasz	}
708255570Strasz
709255570Strasz	memset(&hints, 0, sizeof(hints));
710255570Strasz	hints.ai_family = PF_UNSPEC;
711255570Strasz	hints.ai_socktype = SOCK_STREAM;
712255570Strasz	hints.ai_flags = AI_PASSIVE;
713273635Smav	error = getaddrinfo(addr, port, &hints, ai);
714275452Smav	free(str);
715275452Smav	return ((error != 0) ? 1 : 0);
716273635Smav}
717255570Strasz
718273635Smavint
719273635Smavportal_group_add_listen(struct portal_group *pg, const char *value, bool iser)
720273635Smav{
721273635Smav	struct portal *portal;
722273635Smav
723273635Smav	portal = portal_new(pg);
724273635Smav	portal->p_listen = checked_strdup(value);
725273635Smav	portal->p_iser = iser;
726273635Smav
727273635Smav	if (parse_addr_port(portal->p_listen, "3260", &portal->p_ai)) {
728273635Smav		log_warnx("invalid listen address %s", portal->p_listen);
729271187Strasz		portal_delete(portal);
730255570Strasz		return (1);
731255570Strasz	}
732255570Strasz
733255570Strasz	/*
734255570Strasz	 * XXX: getaddrinfo(3) may return multiple addresses; we should turn
735255570Strasz	 *	those into multiple portals.
736255570Strasz	 */
737255570Strasz
738255570Strasz	return (0);
739255570Strasz}
740255570Strasz
741273635Smavint
742273635Smavisns_new(struct conf *conf, const char *addr)
743273635Smav{
744273635Smav	struct isns *isns;
745273635Smav
746273635Smav	isns = calloc(1, sizeof(*isns));
747273635Smav	if (isns == NULL)
748273635Smav		log_err(1, "calloc");
749273635Smav	isns->i_conf = conf;
750273635Smav	TAILQ_INSERT_TAIL(&conf->conf_isns, isns, i_next);
751273635Smav	isns->i_addr = checked_strdup(addr);
752273635Smav
753273635Smav	if (parse_addr_port(isns->i_addr, "3205", &isns->i_ai)) {
754273635Smav		log_warnx("invalid iSNS address %s", isns->i_addr);
755273635Smav		isns_delete(isns);
756273635Smav		return (1);
757273635Smav	}
758273635Smav
759273635Smav	/*
760273635Smav	 * XXX: getaddrinfo(3) may return multiple addresses; we should turn
761273635Smav	 *	those into multiple servers.
762273635Smav	 */
763273635Smav
764273635Smav	return (0);
765273635Smav}
766273635Smav
767273635Smavvoid
768273635Smavisns_delete(struct isns *isns)
769273635Smav{
770273635Smav
771273635Smav	TAILQ_REMOVE(&isns->i_conf->conf_isns, isns, i_next);
772273635Smav	free(isns->i_addr);
773273635Smav	if (isns->i_ai != NULL)
774273635Smav		freeaddrinfo(isns->i_ai);
775273635Smav	free(isns);
776273635Smav}
777273635Smav
778273635Smavstatic int
779273635Smavisns_do_connect(struct isns *isns)
780273635Smav{
781273635Smav	int s;
782273635Smav
783273635Smav	s = socket(isns->i_ai->ai_family, isns->i_ai->ai_socktype,
784273635Smav	    isns->i_ai->ai_protocol);
785273635Smav	if (s < 0) {
786273635Smav		log_warn("socket(2) failed for %s", isns->i_addr);
787273635Smav		return (-1);
788273635Smav	}
789273635Smav	if (connect(s, isns->i_ai->ai_addr, isns->i_ai->ai_addrlen)) {
790273635Smav		log_warn("connect(2) failed for %s", isns->i_addr);
791273635Smav		close(s);
792273635Smav		return (-1);
793273635Smav	}
794273635Smav	return(s);
795273635Smav}
796273635Smav
797273635Smavstatic int
798273635Smavisns_do_register(struct isns *isns, int s, const char *hostname)
799273635Smav{
800273635Smav	struct conf *conf = isns->i_conf;
801273635Smav	struct target *target;
802273635Smav	struct portal *portal;
803273635Smav	struct portal_group *pg;
804278322Smav	struct port *port;
805273635Smav	struct isns_req *req;
806273635Smav	int res = 0;
807273635Smav	uint32_t error;
808273635Smav
809273635Smav	req = isns_req_create(ISNS_FUNC_DEVATTRREG, ISNS_FLAG_CLIENT);
810273635Smav	isns_req_add_str(req, 32, TAILQ_FIRST(&conf->conf_targets)->t_name);
811273635Smav	isns_req_add_delim(req);
812273635Smav	isns_req_add_str(req, 1, hostname);
813273635Smav	isns_req_add_32(req, 2, 2); /* 2 -- iSCSI */
814273635Smav	isns_req_add_32(req, 6, conf->conf_isns_period);
815273635Smav	TAILQ_FOREACH(pg, &conf->conf_portal_groups, pg_next) {
816273635Smav		if (pg->pg_unassigned)
817273635Smav			continue;
818273635Smav		TAILQ_FOREACH(portal, &pg->pg_portals, p_next) {
819273635Smav			isns_req_add_addr(req, 16, portal->p_ai);
820273635Smav			isns_req_add_port(req, 17, portal->p_ai);
821273635Smav		}
822273635Smav	}
823273635Smav	TAILQ_FOREACH(target, &conf->conf_targets, t_next) {
824273635Smav		isns_req_add_str(req, 32, target->t_name);
825273635Smav		isns_req_add_32(req, 33, 1); /* 1 -- Target*/
826273635Smav		if (target->t_alias != NULL)
827273635Smav			isns_req_add_str(req, 34, target->t_alias);
828278322Smav		TAILQ_FOREACH(port, &target->t_ports, p_ts) {
829278322Smav			if ((pg = port->p_portal_group) == NULL)
830278322Smav				continue;
831278322Smav			isns_req_add_32(req, 51, pg->pg_tag);
832278322Smav			TAILQ_FOREACH(portal, &pg->pg_portals, p_next) {
833278322Smav				isns_req_add_addr(req, 49, portal->p_ai);
834278322Smav				isns_req_add_port(req, 50, portal->p_ai);
835278322Smav			}
836273635Smav		}
837273635Smav	}
838273635Smav	res = isns_req_send(s, req);
839273635Smav	if (res < 0) {
840273635Smav		log_warn("send(2) failed for %s", isns->i_addr);
841273635Smav		goto quit;
842273635Smav	}
843273635Smav	res = isns_req_receive(s, req);
844273635Smav	if (res < 0) {
845273635Smav		log_warn("receive(2) failed for %s", isns->i_addr);
846273635Smav		goto quit;
847273635Smav	}
848273635Smav	error = isns_req_get_status(req);
849273635Smav	if (error != 0) {
850273635Smav		log_warnx("iSNS register error %d for %s", error, isns->i_addr);
851273635Smav		res = -1;
852273635Smav	}
853273635Smavquit:
854273635Smav	isns_req_free(req);
855273635Smav	return (res);
856273635Smav}
857273635Smav
858273635Smavstatic int
859273635Smavisns_do_check(struct isns *isns, int s, const char *hostname)
860273635Smav{
861273635Smav	struct conf *conf = isns->i_conf;
862273635Smav	struct isns_req *req;
863273635Smav	int res = 0;
864273635Smav	uint32_t error;
865273635Smav
866273635Smav	req = isns_req_create(ISNS_FUNC_DEVATTRQRY, ISNS_FLAG_CLIENT);
867273635Smav	isns_req_add_str(req, 32, TAILQ_FIRST(&conf->conf_targets)->t_name);
868273635Smav	isns_req_add_str(req, 1, hostname);
869273635Smav	isns_req_add_delim(req);
870273635Smav	isns_req_add(req, 2, 0, NULL);
871273635Smav	res = isns_req_send(s, req);
872273635Smav	if (res < 0) {
873273635Smav		log_warn("send(2) failed for %s", isns->i_addr);
874273635Smav		goto quit;
875273635Smav	}
876273635Smav	res = isns_req_receive(s, req);
877273635Smav	if (res < 0) {
878273635Smav		log_warn("receive(2) failed for %s", isns->i_addr);
879273635Smav		goto quit;
880273635Smav	}
881273635Smav	error = isns_req_get_status(req);
882273635Smav	if (error != 0) {
883273635Smav		log_warnx("iSNS check error %d for %s", error, isns->i_addr);
884273635Smav		res = -1;
885273635Smav	}
886273635Smavquit:
887273635Smav	isns_req_free(req);
888273635Smav	return (res);
889273635Smav}
890273635Smav
891273635Smavstatic int
892273635Smavisns_do_deregister(struct isns *isns, int s, const char *hostname)
893273635Smav{
894273635Smav	struct conf *conf = isns->i_conf;
895273635Smav	struct isns_req *req;
896273635Smav	int res = 0;
897273635Smav	uint32_t error;
898273635Smav
899273635Smav	req = isns_req_create(ISNS_FUNC_DEVDEREG, ISNS_FLAG_CLIENT);
900273635Smav	isns_req_add_str(req, 32, TAILQ_FIRST(&conf->conf_targets)->t_name);
901273635Smav	isns_req_add_delim(req);
902273635Smav	isns_req_add_str(req, 1, hostname);
903273635Smav	res = isns_req_send(s, req);
904273635Smav	if (res < 0) {
905273635Smav		log_warn("send(2) failed for %s", isns->i_addr);
906273635Smav		goto quit;
907273635Smav	}
908273635Smav	res = isns_req_receive(s, req);
909273635Smav	if (res < 0) {
910273635Smav		log_warn("receive(2) failed for %s", isns->i_addr);
911273635Smav		goto quit;
912273635Smav	}
913273635Smav	error = isns_req_get_status(req);
914273635Smav	if (error != 0) {
915273635Smav		log_warnx("iSNS deregister error %d for %s", error, isns->i_addr);
916273635Smav		res = -1;
917273635Smav	}
918273635Smavquit:
919273635Smav	isns_req_free(req);
920273635Smav	return (res);
921273635Smav}
922273635Smav
923273635Smavvoid
924273635Smavisns_register(struct isns *isns, struct isns *oldisns)
925273635Smav{
926273635Smav	struct conf *conf = isns->i_conf;
927274248Smav	int s;
928273635Smav	char hostname[256];
929273635Smav
930273635Smav	if (TAILQ_EMPTY(&conf->conf_targets) ||
931273635Smav	    TAILQ_EMPTY(&conf->conf_portal_groups))
932273635Smav		return;
933273635Smav	set_timeout(conf->conf_isns_timeout, false);
934273635Smav	s = isns_do_connect(isns);
935273635Smav	if (s < 0) {
936273635Smav		set_timeout(0, false);
937273635Smav		return;
938273635Smav	}
939273635Smav	gethostname(hostname, sizeof(hostname));
940273635Smav
941273635Smav	if (oldisns == NULL || TAILQ_EMPTY(&oldisns->i_conf->conf_targets))
942273635Smav		oldisns = isns;
943274248Smav	isns_do_deregister(oldisns, s, hostname);
944274248Smav	isns_do_register(isns, s, hostname);
945273635Smav	close(s);
946273635Smav	set_timeout(0, false);
947273635Smav}
948273635Smav
949273635Smavvoid
950273635Smavisns_check(struct isns *isns)
951273635Smav{
952273635Smav	struct conf *conf = isns->i_conf;
953273635Smav	int s, res;
954273635Smav	char hostname[256];
955273635Smav
956273635Smav	if (TAILQ_EMPTY(&conf->conf_targets) ||
957273635Smav	    TAILQ_EMPTY(&conf->conf_portal_groups))
958273635Smav		return;
959273635Smav	set_timeout(conf->conf_isns_timeout, false);
960273635Smav	s = isns_do_connect(isns);
961273635Smav	if (s < 0) {
962273635Smav		set_timeout(0, false);
963273635Smav		return;
964273635Smav	}
965273635Smav	gethostname(hostname, sizeof(hostname));
966273635Smav
967273635Smav	res = isns_do_check(isns, s, hostname);
968273635Smav	if (res < 0) {
969274248Smav		isns_do_deregister(isns, s, hostname);
970274248Smav		isns_do_register(isns, s, hostname);
971273635Smav	}
972273635Smav	close(s);
973273635Smav	set_timeout(0, false);
974273635Smav}
975273635Smav
976273635Smavvoid
977273635Smavisns_deregister(struct isns *isns)
978273635Smav{
979273635Smav	struct conf *conf = isns->i_conf;
980274248Smav	int s;
981273635Smav	char hostname[256];
982273635Smav
983273635Smav	if (TAILQ_EMPTY(&conf->conf_targets) ||
984273635Smav	    TAILQ_EMPTY(&conf->conf_portal_groups))
985273635Smav		return;
986273635Smav	set_timeout(conf->conf_isns_timeout, false);
987273635Smav	s = isns_do_connect(isns);
988273635Smav	if (s < 0)
989273635Smav		return;
990273635Smav	gethostname(hostname, sizeof(hostname));
991273635Smav
992274248Smav	isns_do_deregister(isns, s, hostname);
993273635Smav	close(s);
994273635Smav	set_timeout(0, false);
995273635Smav}
996273635Smav
997273813Straszint
998273816Straszportal_group_set_filter(struct portal_group *pg, const char *str)
999273813Strasz{
1000273816Strasz	int filter;
1001273813Strasz
1002273813Strasz	if (strcmp(str, "none") == 0) {
1003273813Strasz		filter = PG_FILTER_NONE;
1004273813Strasz	} else if (strcmp(str, "portal") == 0) {
1005273813Strasz		filter = PG_FILTER_PORTAL;
1006273813Strasz	} else if (strcmp(str, "portal-name") == 0) {
1007273813Strasz		filter = PG_FILTER_PORTAL_NAME;
1008273813Strasz	} else if (strcmp(str, "portal-name-auth") == 0) {
1009273813Strasz		filter = PG_FILTER_PORTAL_NAME_AUTH;
1010273813Strasz	} else {
1011273813Strasz		log_warnx("invalid discovery-filter \"%s\" for portal-group "
1012273813Strasz		    "\"%s\"; valid values are \"none\", \"portal\", "
1013273813Strasz		    "\"portal-name\", and \"portal-name-auth\"",
1014273813Strasz		    str, pg->pg_name);
1015273813Strasz		return (1);
1016273813Strasz	}
1017273813Strasz
1018273816Strasz	if (pg->pg_discovery_filter != PG_FILTER_UNKNOWN &&
1019273816Strasz	    pg->pg_discovery_filter != filter) {
1020273813Strasz		log_warnx("cannot set discovery-filter to \"%s\" for "
1021273813Strasz		    "portal-group \"%s\"; already has a different "
1022273813Strasz		    "value", str, pg->pg_name);
1023273813Strasz		return (1);
1024273813Strasz	}
1025273813Strasz
1026273816Strasz	pg->pg_discovery_filter = filter;
1027273816Strasz
1028273816Strasz	return (0);
1029273813Strasz}
1030273813Strasz
1031274308Straszint
1032279392Straszportal_group_set_offload(struct portal_group *pg, const char *offload)
1033279392Strasz{
1034279392Strasz
1035279392Strasz	if (pg->pg_offload != NULL) {
1036279392Strasz		log_warnx("cannot set offload to \"%s\" for "
1037279392Strasz		    "portal-group \"%s\"; already defined",
1038279392Strasz		    offload, pg->pg_name);
1039279392Strasz		return (1);
1040279392Strasz	}
1041279392Strasz
1042279392Strasz	pg->pg_offload = checked_strdup(offload);
1043279392Strasz
1044279392Strasz	return (0);
1045279392Strasz}
1046279392Strasz
1047279392Straszint
1048274308Straszportal_group_set_redirection(struct portal_group *pg, const char *addr)
1049274308Strasz{
1050274308Strasz
1051274308Strasz	if (pg->pg_redirection != NULL) {
1052274308Strasz		log_warnx("cannot set redirection to \"%s\" for "
1053274308Strasz		    "portal-group \"%s\"; already defined",
1054274308Strasz		    addr, pg->pg_name);
1055274308Strasz		return (1);
1056274308Strasz	}
1057274308Strasz
1058274308Strasz	pg->pg_redirection = checked_strdup(addr);
1059274308Strasz
1060274308Strasz	return (0);
1061274308Strasz}
1062274308Strasz
1063255570Straszstatic bool
1064255570Straszvalid_hex(const char ch)
1065255570Strasz{
1066255570Strasz	switch (ch) {
1067255570Strasz	case '0':
1068255570Strasz	case '1':
1069255570Strasz	case '2':
1070255570Strasz	case '3':
1071255570Strasz	case '4':
1072255570Strasz	case '5':
1073255570Strasz	case '6':
1074255570Strasz	case '7':
1075255570Strasz	case '8':
1076255570Strasz	case '9':
1077255570Strasz	case 'a':
1078255570Strasz	case 'A':
1079255570Strasz	case 'b':
1080255570Strasz	case 'B':
1081255570Strasz	case 'c':
1082255570Strasz	case 'C':
1083255570Strasz	case 'd':
1084255570Strasz	case 'D':
1085255570Strasz	case 'e':
1086255570Strasz	case 'E':
1087255570Strasz	case 'f':
1088255570Strasz	case 'F':
1089255570Strasz		return (true);
1090255570Strasz	default:
1091255570Strasz		return (false);
1092255570Strasz	}
1093255570Strasz}
1094255570Strasz
1095255570Straszbool
1096255570Straszvalid_iscsi_name(const char *name)
1097255570Strasz{
1098255570Strasz	int i;
1099255570Strasz
1100255570Strasz	if (strlen(name) >= MAX_NAME_LEN) {
1101255570Strasz		log_warnx("overlong name for target \"%s\"; max length allowed "
1102255570Strasz		    "by iSCSI specification is %d characters",
1103255570Strasz		    name, MAX_NAME_LEN);
1104255570Strasz		return (false);
1105255570Strasz	}
1106255570Strasz
1107255570Strasz	/*
1108255570Strasz	 * In the cases below, we don't return an error, just in case the admin
1109255570Strasz	 * was right, and we're wrong.
1110255570Strasz	 */
1111255570Strasz	if (strncasecmp(name, "iqn.", strlen("iqn.")) == 0) {
1112255570Strasz		for (i = strlen("iqn."); name[i] != '\0'; i++) {
1113255570Strasz			/*
1114255570Strasz			 * XXX: We should verify UTF-8 normalisation, as defined
1115273464Strasz			 *      by 3.2.6.2: iSCSI Name Encoding.
1116255570Strasz			 */
1117255570Strasz			if (isalnum(name[i]))
1118255570Strasz				continue;
1119255570Strasz			if (name[i] == '-' || name[i] == '.' || name[i] == ':')
1120255570Strasz				continue;
1121255570Strasz			log_warnx("invalid character \"%c\" in target name "
1122255570Strasz			    "\"%s\"; allowed characters are letters, digits, "
1123255570Strasz			    "'-', '.', and ':'", name[i], name);
1124255570Strasz			break;
1125255570Strasz		}
1126255570Strasz		/*
1127255570Strasz		 * XXX: Check more stuff: valid date and a valid reversed domain.
1128255570Strasz		 */
1129255570Strasz	} else if (strncasecmp(name, "eui.", strlen("eui.")) == 0) {
1130255570Strasz		if (strlen(name) != strlen("eui.") + 16)
1131255570Strasz			log_warnx("invalid target name \"%s\"; the \"eui.\" "
1132255570Strasz			    "should be followed by exactly 16 hexadecimal "
1133255570Strasz			    "digits", name);
1134255570Strasz		for (i = strlen("eui."); name[i] != '\0'; i++) {
1135255570Strasz			if (!valid_hex(name[i])) {
1136255570Strasz				log_warnx("invalid character \"%c\" in target "
1137255570Strasz				    "name \"%s\"; allowed characters are 1-9 "
1138255570Strasz				    "and A-F", name[i], name);
1139255570Strasz				break;
1140255570Strasz			}
1141255570Strasz		}
1142255570Strasz	} else if (strncasecmp(name, "naa.", strlen("naa.")) == 0) {
1143255570Strasz		if (strlen(name) > strlen("naa.") + 32)
1144255570Strasz			log_warnx("invalid target name \"%s\"; the \"naa.\" "
1145255570Strasz			    "should be followed by at most 32 hexadecimal "
1146255570Strasz			    "digits", name);
1147255570Strasz		for (i = strlen("naa."); name[i] != '\0'; i++) {
1148255570Strasz			if (!valid_hex(name[i])) {
1149255570Strasz				log_warnx("invalid character \"%c\" in target "
1150255570Strasz				    "name \"%s\"; allowed characters are 1-9 "
1151255570Strasz				    "and A-F", name[i], name);
1152255570Strasz				break;
1153255570Strasz			}
1154255570Strasz		}
1155255570Strasz	} else {
1156255570Strasz		log_warnx("invalid target name \"%s\"; should start with "
1157288208Sjpaetzel		    "either \"iqn.\", \"eui.\", or \"naa.\"",
1158255570Strasz		    name);
1159255570Strasz	}
1160255570Strasz	return (true);
1161255570Strasz}
1162255570Strasz
1163278354Smavstruct pport *
1164278354Smavpport_new(struct conf *conf, const char *name, uint32_t ctl_port)
1165278354Smav{
1166278354Smav	struct pport *pp;
1167278354Smav
1168278354Smav	pp = calloc(1, sizeof(*pp));
1169278354Smav	if (pp == NULL)
1170278354Smav		log_err(1, "calloc");
1171278354Smav	pp->pp_conf = conf;
1172278354Smav	pp->pp_name = checked_strdup(name);
1173278354Smav	pp->pp_ctl_port = ctl_port;
1174278354Smav	TAILQ_INIT(&pp->pp_ports);
1175278354Smav	TAILQ_INSERT_TAIL(&conf->conf_pports, pp, pp_next);
1176278354Smav	return (pp);
1177278354Smav}
1178278354Smav
1179278354Smavstruct pport *
1180278354Smavpport_find(const struct conf *conf, const char *name)
1181278354Smav{
1182278354Smav	struct pport *pp;
1183278354Smav
1184278354Smav	TAILQ_FOREACH(pp, &conf->conf_pports, pp_next) {
1185278354Smav		if (strcasecmp(pp->pp_name, name) == 0)
1186278354Smav			return (pp);
1187278354Smav	}
1188278354Smav	return (NULL);
1189278354Smav}
1190278354Smav
1191278354Smavstruct pport *
1192278354Smavpport_copy(struct pport *pp, struct conf *conf)
1193278354Smav{
1194278354Smav	struct pport *ppnew;
1195278354Smav
1196278354Smav	ppnew = pport_new(conf, pp->pp_name, pp->pp_ctl_port);
1197278354Smav	return (ppnew);
1198278354Smav}
1199278354Smav
1200278354Smavvoid
1201278354Smavpport_delete(struct pport *pp)
1202278354Smav{
1203278354Smav	struct port *port, *tport;
1204278354Smav
1205278354Smav	TAILQ_FOREACH_SAFE(port, &pp->pp_ports, p_ts, tport)
1206278354Smav		port_delete(port);
1207278354Smav	TAILQ_REMOVE(&pp->pp_conf->conf_pports, pp, pp_next);
1208278354Smav	free(pp->pp_name);
1209278354Smav	free(pp);
1210278354Smav}
1211278354Smav
1212278322Smavstruct port *
1213278322Smavport_new(struct conf *conf, struct target *target, struct portal_group *pg)
1214278322Smav{
1215278322Smav	struct port *port;
1216278354Smav	char *name;
1217278594Smav	int ret;
1218278322Smav
1219278594Smav	ret = asprintf(&name, "%s-%s", pg->pg_name, target->t_name);
1220278594Smav	if (ret <= 0)
1221278594Smav		log_err(1, "asprintf");
1222278354Smav	if (port_find(conf, name) != NULL) {
1223278354Smav		log_warnx("duplicate port \"%s\"", name);
1224278354Smav		free(name);
1225278354Smav		return (NULL);
1226278354Smav	}
1227278322Smav	port = calloc(1, sizeof(*port));
1228278322Smav	if (port == NULL)
1229278322Smav		log_err(1, "calloc");
1230278322Smav	port->p_conf = conf;
1231278354Smav	port->p_name = name;
1232278322Smav	TAILQ_INSERT_TAIL(&conf->conf_ports, port, p_next);
1233278322Smav	TAILQ_INSERT_TAIL(&target->t_ports, port, p_ts);
1234278322Smav	port->p_target = target;
1235278322Smav	TAILQ_INSERT_TAIL(&pg->pg_ports, port, p_pgs);
1236278322Smav	port->p_portal_group = pg;
1237287534Smav	port->p_foreign = pg->pg_foreign;
1238278322Smav	return (port);
1239278322Smav}
1240278322Smav
1241278322Smavstruct port *
1242278354Smavport_new_pp(struct conf *conf, struct target *target, struct pport *pp)
1243278354Smav{
1244278354Smav	struct port *port;
1245278354Smav	char *name;
1246278594Smav	int ret;
1247278354Smav
1248278594Smav	ret = asprintf(&name, "%s-%s", pp->pp_name, target->t_name);
1249278594Smav	if (ret <= 0)
1250278594Smav		log_err(1, "asprintf");
1251278354Smav	if (port_find(conf, name) != NULL) {
1252278354Smav		log_warnx("duplicate port \"%s\"", name);
1253278354Smav		free(name);
1254278354Smav		return (NULL);
1255278354Smav	}
1256278354Smav	port = calloc(1, sizeof(*port));
1257278354Smav	if (port == NULL)
1258278354Smav		log_err(1, "calloc");
1259278354Smav	port->p_conf = conf;
1260278354Smav	port->p_name = name;
1261278354Smav	TAILQ_INSERT_TAIL(&conf->conf_ports, port, p_next);
1262278354Smav	TAILQ_INSERT_TAIL(&target->t_ports, port, p_ts);
1263278354Smav	port->p_target = target;
1264278354Smav	TAILQ_INSERT_TAIL(&pp->pp_ports, port, p_pps);
1265278354Smav	port->p_pport = pp;
1266278354Smav	return (port);
1267278354Smav}
1268278354Smav
1269278354Smavstruct port *
1270278322Smavport_find(const struct conf *conf, const char *name)
1271278322Smav{
1272278322Smav	struct port *port;
1273278322Smav
1274278322Smav	TAILQ_FOREACH(port, &conf->conf_ports, p_next) {
1275278322Smav		if (strcasecmp(port->p_name, name) == 0)
1276278322Smav			return (port);
1277278322Smav	}
1278278322Smav
1279278322Smav	return (NULL);
1280278322Smav}
1281278322Smav
1282278322Smavstruct port *
1283278322Smavport_find_in_pg(const struct portal_group *pg, const char *target)
1284278322Smav{
1285278322Smav	struct port *port;
1286278322Smav
1287278322Smav	TAILQ_FOREACH(port, &pg->pg_ports, p_pgs) {
1288278322Smav		if (strcasecmp(port->p_target->t_name, target) == 0)
1289278322Smav			return (port);
1290278322Smav	}
1291278322Smav
1292278322Smav	return (NULL);
1293278322Smav}
1294278322Smav
1295278322Smavvoid
1296278322Smavport_delete(struct port *port)
1297278322Smav{
1298278322Smav
1299278322Smav	if (port->p_portal_group)
1300278322Smav		TAILQ_REMOVE(&port->p_portal_group->pg_ports, port, p_pgs);
1301278354Smav	if (port->p_pport)
1302278354Smav		TAILQ_REMOVE(&port->p_pport->pp_ports, port, p_pps);
1303278322Smav	if (port->p_target)
1304278322Smav		TAILQ_REMOVE(&port->p_target->t_ports, port, p_ts);
1305278322Smav	TAILQ_REMOVE(&port->p_conf->conf_ports, port, p_next);
1306278322Smav	free(port->p_name);
1307278322Smav	free(port);
1308278322Smav}
1309278322Smav
1310255570Straszstruct target *
1311261757Strasztarget_new(struct conf *conf, const char *name)
1312255570Strasz{
1313255570Strasz	struct target *targ;
1314255570Strasz	int i, len;
1315255570Strasz
1316261757Strasz	targ = target_find(conf, name);
1317255570Strasz	if (targ != NULL) {
1318261757Strasz		log_warnx("duplicated target \"%s\"", name);
1319255570Strasz		return (NULL);
1320255570Strasz	}
1321261757Strasz	if (valid_iscsi_name(name) == false) {
1322261757Strasz		log_warnx("target name \"%s\" is invalid", name);
1323255570Strasz		return (NULL);
1324255570Strasz	}
1325255570Strasz	targ = calloc(1, sizeof(*targ));
1326255570Strasz	if (targ == NULL)
1327255570Strasz		log_err(1, "calloc");
1328261757Strasz	targ->t_name = checked_strdup(name);
1329255570Strasz
1330255570Strasz	/*
1331255570Strasz	 * RFC 3722 requires us to normalize the name to lowercase.
1332255570Strasz	 */
1333261757Strasz	len = strlen(name);
1334255570Strasz	for (i = 0; i < len; i++)
1335261757Strasz		targ->t_name[i] = tolower(targ->t_name[i]);
1336255570Strasz
1337255570Strasz	targ->t_conf = conf;
1338278322Smav	TAILQ_INIT(&targ->t_ports);
1339255570Strasz	TAILQ_INSERT_TAIL(&conf->conf_targets, targ, t_next);
1340255570Strasz
1341255570Strasz	return (targ);
1342255570Strasz}
1343255570Strasz
1344255570Straszvoid
1345255570Strasztarget_delete(struct target *targ)
1346255570Strasz{
1347278322Smav	struct port *port, *tport;
1348255570Strasz
1349278322Smav	TAILQ_FOREACH_SAFE(port, &targ->t_ports, p_ts, tport)
1350278322Smav		port_delete(port);
1351255570Strasz	TAILQ_REMOVE(&targ->t_conf->conf_targets, targ, t_next);
1352255570Strasz
1353261757Strasz	free(targ->t_name);
1354274308Strasz	free(targ->t_redirection);
1355255570Strasz	free(targ);
1356255570Strasz}
1357255570Strasz
1358255570Straszstruct target *
1359261757Strasztarget_find(struct conf *conf, const char *name)
1360255570Strasz{
1361255570Strasz	struct target *targ;
1362255570Strasz
1363255570Strasz	TAILQ_FOREACH(targ, &conf->conf_targets, t_next) {
1364261757Strasz		if (strcasecmp(targ->t_name, name) == 0)
1365255570Strasz			return (targ);
1366255570Strasz	}
1367255570Strasz
1368255570Strasz	return (NULL);
1369255570Strasz}
1370255570Strasz
1371274308Straszint
1372274308Strasztarget_set_redirection(struct target *target, const char *addr)
1373274308Strasz{
1374274308Strasz
1375274308Strasz	if (target->t_redirection != NULL) {
1376274308Strasz		log_warnx("cannot set redirection to \"%s\" for "
1377274308Strasz		    "target \"%s\"; already defined",
1378274308Strasz		    addr, target->t_name);
1379274308Strasz		return (1);
1380274308Strasz	}
1381274308Strasz
1382274308Strasz	target->t_redirection = checked_strdup(addr);
1383274308Strasz
1384274308Strasz	return (0);
1385274308Strasz}
1386274308Strasz
1387255570Straszstruct lun *
1388278037Smavlun_new(struct conf *conf, const char *name)
1389255570Strasz{
1390255570Strasz	struct lun *lun;
1391255570Strasz
1392278037Smav	lun = lun_find(conf, name);
1393255570Strasz	if (lun != NULL) {
1394278037Smav		log_warnx("duplicated lun \"%s\"", name);
1395255570Strasz		return (NULL);
1396255570Strasz	}
1397255570Strasz
1398255570Strasz	lun = calloc(1, sizeof(*lun));
1399255570Strasz	if (lun == NULL)
1400255570Strasz		log_err(1, "calloc");
1401278037Smav	lun->l_conf = conf;
1402278037Smav	lun->l_name = checked_strdup(name);
1403255570Strasz	TAILQ_INIT(&lun->l_options);
1404278037Smav	TAILQ_INSERT_TAIL(&conf->conf_luns, lun, l_next);
1405287823Smav	lun->l_ctl_lun = -1;
1406255570Strasz
1407255570Strasz	return (lun);
1408255570Strasz}
1409255570Strasz
1410255570Straszvoid
1411255570Straszlun_delete(struct lun *lun)
1412255570Strasz{
1413278037Smav	struct target *targ;
1414290615Smav	struct option *o, *tmp;
1415278037Smav	int i;
1416255570Strasz
1417278037Smav	TAILQ_FOREACH(targ, &lun->l_conf->conf_targets, t_next) {
1418278037Smav		for (i = 0; i < MAX_LUNS; i++) {
1419278037Smav			if (targ->t_luns[i] == lun)
1420278037Smav				targ->t_luns[i] = NULL;
1421278037Smav		}
1422278037Smav	}
1423278037Smav	TAILQ_REMOVE(&lun->l_conf->conf_luns, lun, l_next);
1424255570Strasz
1425290615Smav	TAILQ_FOREACH_SAFE(o, &lun->l_options, o_next, tmp)
1426290615Smav		option_delete(&lun->l_options, o);
1427278037Smav	free(lun->l_name);
1428255570Strasz	free(lun->l_backend);
1429255570Strasz	free(lun->l_device_id);
1430255570Strasz	free(lun->l_path);
1431278037Smav	free(lun->l_scsiname);
1432255570Strasz	free(lun->l_serial);
1433255570Strasz	free(lun);
1434255570Strasz}
1435255570Strasz
1436255570Straszstruct lun *
1437278037Smavlun_find(const struct conf *conf, const char *name)
1438255570Strasz{
1439255570Strasz	struct lun *lun;
1440255570Strasz
1441278037Smav	TAILQ_FOREACH(lun, &conf->conf_luns, l_next) {
1442278037Smav		if (strcmp(lun->l_name, name) == 0)
1443255570Strasz			return (lun);
1444255570Strasz	}
1445255570Strasz
1446255570Strasz	return (NULL);
1447255570Strasz}
1448255570Strasz
1449255570Straszvoid
1450255570Straszlun_set_backend(struct lun *lun, const char *value)
1451255570Strasz{
1452255570Strasz	free(lun->l_backend);
1453255570Strasz	lun->l_backend = checked_strdup(value);
1454255570Strasz}
1455255570Strasz
1456255570Straszvoid
1457255570Straszlun_set_blocksize(struct lun *lun, size_t value)
1458255570Strasz{
1459255570Strasz
1460255570Strasz	lun->l_blocksize = value;
1461255570Strasz}
1462255570Strasz
1463255570Straszvoid
1464288310Smavlun_set_device_type(struct lun *lun, uint8_t value)
1465288310Smav{
1466288310Smav
1467288310Smav	lun->l_device_type = value;
1468288310Smav}
1469288310Smav
1470288310Smavvoid
1471255570Straszlun_set_device_id(struct lun *lun, const char *value)
1472255570Strasz{
1473255570Strasz	free(lun->l_device_id);
1474255570Strasz	lun->l_device_id = checked_strdup(value);
1475255570Strasz}
1476255570Strasz
1477255570Straszvoid
1478255570Straszlun_set_path(struct lun *lun, const char *value)
1479255570Strasz{
1480255570Strasz	free(lun->l_path);
1481255570Strasz	lun->l_path = checked_strdup(value);
1482255570Strasz}
1483255570Strasz
1484255570Straszvoid
1485278037Smavlun_set_scsiname(struct lun *lun, const char *value)
1486278037Smav{
1487278037Smav	free(lun->l_scsiname);
1488278037Smav	lun->l_scsiname = checked_strdup(value);
1489278037Smav}
1490278037Smav
1491278037Smavvoid
1492255570Straszlun_set_serial(struct lun *lun, const char *value)
1493255570Strasz{
1494255570Strasz	free(lun->l_serial);
1495255570Strasz	lun->l_serial = checked_strdup(value);
1496255570Strasz}
1497255570Strasz
1498255570Straszvoid
1499255570Straszlun_set_size(struct lun *lun, size_t value)
1500255570Strasz{
1501255570Strasz
1502255570Strasz	lun->l_size = value;
1503255570Strasz}
1504255570Strasz
1505255570Straszvoid
1506255570Straszlun_set_ctl_lun(struct lun *lun, uint32_t value)
1507255570Strasz{
1508255570Strasz
1509255570Strasz	lun->l_ctl_lun = value;
1510255570Strasz}
1511255570Strasz
1512290615Smavstruct option *
1513290615Smavoption_new(struct options *options, const char *name, const char *value)
1514255570Strasz{
1515290615Smav	struct option *o;
1516255570Strasz
1517290615Smav	o = option_find(options, name);
1518290615Smav	if (o != NULL) {
1519290615Smav		log_warnx("duplicated option \"%s\"", name);
1520255570Strasz		return (NULL);
1521255570Strasz	}
1522255570Strasz
1523290615Smav	o = calloc(1, sizeof(*o));
1524290615Smav	if (o == NULL)
1525255570Strasz		log_err(1, "calloc");
1526290615Smav	o->o_name = checked_strdup(name);
1527290615Smav	o->o_value = checked_strdup(value);
1528290615Smav	TAILQ_INSERT_TAIL(options, o, o_next);
1529255570Strasz
1530290615Smav	return (o);
1531255570Strasz}
1532255570Strasz
1533255570Straszvoid
1534290615Smavoption_delete(struct options *options, struct option *o)
1535255570Strasz{
1536255570Strasz
1537290615Smav	TAILQ_REMOVE(options, o, o_next);
1538290615Smav	free(o->o_name);
1539290615Smav	free(o->o_value);
1540290615Smav	free(o);
1541255570Strasz}
1542255570Strasz
1543290615Smavstruct option *
1544290615Smavoption_find(const struct options *options, const char *name)
1545255570Strasz{
1546290615Smav	struct option *o;
1547255570Strasz
1548290615Smav	TAILQ_FOREACH(o, options, o_next) {
1549290615Smav		if (strcmp(o->o_name, name) == 0)
1550290615Smav			return (o);
1551255570Strasz	}
1552255570Strasz
1553255570Strasz	return (NULL);
1554255570Strasz}
1555255570Strasz
1556255570Straszvoid
1557290615Smavoption_set(struct option *o, const char *value)
1558255570Strasz{
1559255570Strasz
1560290615Smav	free(o->o_value);
1561290615Smav	o->o_value = checked_strdup(value);
1562255570Strasz}
1563255570Strasz
1564255570Straszstatic struct connection *
1565269183Smavconnection_new(struct portal *portal, int fd, const char *host,
1566269183Smav    const struct sockaddr *client_sa)
1567255570Strasz{
1568255570Strasz	struct connection *conn;
1569255570Strasz
1570255570Strasz	conn = calloc(1, sizeof(*conn));
1571255570Strasz	if (conn == NULL)
1572255570Strasz		log_err(1, "calloc");
1573255570Strasz	conn->conn_portal = portal;
1574255570Strasz	conn->conn_socket = fd;
1575255570Strasz	conn->conn_initiator_addr = checked_strdup(host);
1576269183Smav	memcpy(&conn->conn_initiator_sa, client_sa, client_sa->sa_len);
1577255570Strasz
1578255570Strasz	/*
1579255570Strasz	 * Default values, from RFC 3720, section 12.
1580255570Strasz	 */
1581255570Strasz	conn->conn_max_data_segment_length = 8192;
1582255570Strasz	conn->conn_max_burst_length = 262144;
1583255570Strasz	conn->conn_immediate_data = true;
1584255570Strasz
1585255570Strasz	return (conn);
1586255570Strasz}
1587255570Strasz
1588255570Strasz#if 0
1589255570Straszstatic void
1590255570Straszconf_print(struct conf *conf)
1591255570Strasz{
1592255570Strasz	struct auth_group *ag;
1593255570Strasz	struct auth *auth;
1594261754Strasz	struct auth_name *auth_name;
1595261754Strasz	struct auth_portal *auth_portal;
1596255570Strasz	struct portal_group *pg;
1597255570Strasz	struct portal *portal;
1598255570Strasz	struct target *targ;
1599255570Strasz	struct lun *lun;
1600290615Smav	struct option *o;
1601255570Strasz
1602255570Strasz	TAILQ_FOREACH(ag, &conf->conf_auth_groups, ag_next) {
1603255570Strasz		fprintf(stderr, "auth-group %s {\n", ag->ag_name);
1604255570Strasz		TAILQ_FOREACH(auth, &ag->ag_auths, a_next)
1605255570Strasz			fprintf(stderr, "\t chap-mutual %s %s %s %s\n",
1606255570Strasz			    auth->a_user, auth->a_secret,
1607255570Strasz			    auth->a_mutual_user, auth->a_mutual_secret);
1608261754Strasz		TAILQ_FOREACH(auth_name, &ag->ag_names, an_next)
1609261754Strasz			fprintf(stderr, "\t initiator-name %s\n",
1610261754Strasz			    auth_name->an_initator_name);
1611261754Strasz		TAILQ_FOREACH(auth_portal, &ag->ag_portals, an_next)
1612261754Strasz			fprintf(stderr, "\t initiator-portal %s\n",
1613261754Strasz			    auth_portal->an_initator_portal);
1614255570Strasz		fprintf(stderr, "}\n");
1615255570Strasz	}
1616255570Strasz	TAILQ_FOREACH(pg, &conf->conf_portal_groups, pg_next) {
1617255570Strasz		fprintf(stderr, "portal-group %s {\n", pg->pg_name);
1618255570Strasz		TAILQ_FOREACH(portal, &pg->pg_portals, p_next)
1619255570Strasz			fprintf(stderr, "\t listen %s\n", portal->p_listen);
1620255570Strasz		fprintf(stderr, "}\n");
1621255570Strasz	}
1622278037Smav	TAILQ_FOREACH(lun, &conf->conf_luns, l_next) {
1623278037Smav		fprintf(stderr, "\tlun %s {\n", lun->l_name);
1624278037Smav		fprintf(stderr, "\t\tpath %s\n", lun->l_path);
1625290615Smav		TAILQ_FOREACH(o, &lun->l_options, o_next)
1626278037Smav			fprintf(stderr, "\t\toption %s %s\n",
1627290615Smav			    lo->o_name, lo->o_value);
1628278037Smav		fprintf(stderr, "\t}\n");
1629278037Smav	}
1630255570Strasz	TAILQ_FOREACH(targ, &conf->conf_targets, t_next) {
1631261757Strasz		fprintf(stderr, "target %s {\n", targ->t_name);
1632255570Strasz		if (targ->t_alias != NULL)
1633255570Strasz			fprintf(stderr, "\t alias %s\n", targ->t_alias);
1634255570Strasz		fprintf(stderr, "}\n");
1635255570Strasz	}
1636255570Strasz}
1637255570Strasz#endif
1638255570Strasz
1639261749Straszstatic int
1640261749Straszconf_verify_lun(struct lun *lun)
1641261749Strasz{
1642261749Strasz	const struct lun *lun2;
1643261749Strasz
1644261749Strasz	if (lun->l_backend == NULL)
1645261749Strasz		lun_set_backend(lun, "block");
1646261749Strasz	if (strcmp(lun->l_backend, "block") == 0) {
1647261749Strasz		if (lun->l_path == NULL) {
1648278037Smav			log_warnx("missing path for lun \"%s\"",
1649278037Smav			    lun->l_name);
1650261749Strasz			return (1);
1651261749Strasz		}
1652261749Strasz	} else if (strcmp(lun->l_backend, "ramdisk") == 0) {
1653261749Strasz		if (lun->l_size == 0) {
1654278037Smav			log_warnx("missing size for ramdisk-backed lun \"%s\"",
1655278037Smav			    lun->l_name);
1656261749Strasz			return (1);
1657261749Strasz		}
1658261749Strasz		if (lun->l_path != NULL) {
1659261749Strasz			log_warnx("path must not be specified "
1660278037Smav			    "for ramdisk-backed lun \"%s\"",
1661278037Smav			    lun->l_name);
1662261749Strasz			return (1);
1663261749Strasz		}
1664261749Strasz	}
1665261749Strasz	if (lun->l_blocksize == 0) {
1666288486Smav		if (lun->l_device_type == 5)
1667288486Smav			lun_set_blocksize(lun, DEFAULT_CD_BLOCKSIZE);
1668288486Smav		else
1669288486Smav			lun_set_blocksize(lun, DEFAULT_BLOCKSIZE);
1670261749Strasz	} else if (lun->l_blocksize < 0) {
1671278037Smav		log_warnx("invalid blocksize for lun \"%s\"; "
1672278037Smav		    "must be larger than 0", lun->l_name);
1673261749Strasz		return (1);
1674261749Strasz	}
1675261749Strasz	if (lun->l_size != 0 && lun->l_size % lun->l_blocksize != 0) {
1676278037Smav		log_warnx("invalid size for lun \"%s\"; "
1677278037Smav		    "must be multiple of blocksize", lun->l_name);
1678261749Strasz		return (1);
1679261749Strasz	}
1680278037Smav	TAILQ_FOREACH(lun2, &lun->l_conf->conf_luns, l_next) {
1681278037Smav		if (lun == lun2)
1682278037Smav			continue;
1683278037Smav		if (lun->l_path != NULL && lun2->l_path != NULL &&
1684278037Smav		    strcmp(lun->l_path, lun2->l_path) == 0) {
1685278037Smav			log_debugx("WARNING: path \"%s\" duplicated "
1686278037Smav			    "between lun \"%s\", and "
1687278037Smav			    "lun \"%s\"", lun->l_path,
1688278037Smav			    lun->l_name, lun2->l_name);
1689261750Strasz		}
1690261750Strasz	}
1691261749Strasz
1692261749Strasz	return (0);
1693261749Strasz}
1694261749Strasz
1695255570Straszint
1696255570Straszconf_verify(struct conf *conf)
1697255570Strasz{
1698255570Strasz	struct auth_group *ag;
1699255570Strasz	struct portal_group *pg;
1700278322Smav	struct port *port;
1701255570Strasz	struct target *targ;
1702261749Strasz	struct lun *lun;
1703273465Strasz	bool found;
1704278037Smav	int error, i;
1705255570Strasz
1706255570Strasz	if (conf->conf_pidfile_path == NULL)
1707255570Strasz		conf->conf_pidfile_path = checked_strdup(DEFAULT_PIDFILE);
1708255570Strasz
1709278037Smav	TAILQ_FOREACH(lun, &conf->conf_luns, l_next) {
1710278037Smav		error = conf_verify_lun(lun);
1711278037Smav		if (error != 0)
1712278037Smav			return (error);
1713278037Smav	}
1714255570Strasz	TAILQ_FOREACH(targ, &conf->conf_targets, t_next) {
1715255570Strasz		if (targ->t_auth_group == NULL) {
1716261760Strasz			targ->t_auth_group = auth_group_find(conf,
1717261760Strasz			    "default");
1718261760Strasz			assert(targ->t_auth_group != NULL);
1719255570Strasz		}
1720278322Smav		if (TAILQ_EMPTY(&targ->t_ports)) {
1721278322Smav			pg = portal_group_find(conf, "default");
1722278322Smav			assert(pg != NULL);
1723278322Smav			port_new(conf, targ, pg);
1724255570Strasz		}
1725273465Strasz		found = false;
1726278037Smav		for (i = 0; i < MAX_LUNS; i++) {
1727278037Smav			if (targ->t_luns[i] != NULL)
1728278037Smav				found = true;
1729255570Strasz		}
1730274308Strasz		if (!found && targ->t_redirection == NULL) {
1731264500Strasz			log_warnx("no LUNs defined for target \"%s\"",
1732264500Strasz			    targ->t_name);
1733255570Strasz		}
1734274308Strasz		if (found && targ->t_redirection != NULL) {
1735274308Strasz			log_debugx("target \"%s\" contains luns, "
1736274308Strasz			    " but configured for redirection",
1737274308Strasz			    targ->t_name);
1738274308Strasz		}
1739255570Strasz	}
1740255570Strasz	TAILQ_FOREACH(pg, &conf->conf_portal_groups, pg_next) {
1741255570Strasz		assert(pg->pg_name != NULL);
1742255570Strasz		if (pg->pg_discovery_auth_group == NULL) {
1743255570Strasz			pg->pg_discovery_auth_group =
1744261762Strasz			    auth_group_find(conf, "default");
1745255570Strasz			assert(pg->pg_discovery_auth_group != NULL);
1746255570Strasz		}
1747255570Strasz
1748273813Strasz		if (pg->pg_discovery_filter == PG_FILTER_UNKNOWN)
1749273813Strasz			pg->pg_discovery_filter = PG_FILTER_NONE;
1750273813Strasz
1751287534Smav		if (pg->pg_redirection != NULL) {
1752287534Smav			if (!TAILQ_EMPTY(&pg->pg_ports)) {
1753274308Strasz				log_debugx("portal-group \"%s\" assigned "
1754278322Smav				    "to target, but configured "
1755274308Strasz				    "for redirection",
1756278322Smav				    pg->pg_name);
1757274308Strasz			}
1758274308Strasz			pg->pg_unassigned = false;
1759287534Smav		} else if (!TAILQ_EMPTY(&pg->pg_ports)) {
1760287534Smav			pg->pg_unassigned = false;
1761274308Strasz		} else {
1762255570Strasz			if (strcmp(pg->pg_name, "default") != 0)
1763255570Strasz				log_warnx("portal-group \"%s\" not assigned "
1764255570Strasz				    "to any target", pg->pg_name);
1765255570Strasz			pg->pg_unassigned = true;
1766274308Strasz		}
1767255570Strasz	}
1768255570Strasz	TAILQ_FOREACH(ag, &conf->conf_auth_groups, ag_next) {
1769255570Strasz		if (ag->ag_name == NULL)
1770255570Strasz			assert(ag->ag_target != NULL);
1771255570Strasz		else
1772255570Strasz			assert(ag->ag_target == NULL);
1773255570Strasz
1774273465Strasz		found = false;
1775255570Strasz		TAILQ_FOREACH(targ, &conf->conf_targets, t_next) {
1776273465Strasz			if (targ->t_auth_group == ag) {
1777273465Strasz				found = true;
1778255570Strasz				break;
1779273465Strasz			}
1780255570Strasz		}
1781278322Smav		TAILQ_FOREACH(port, &conf->conf_ports, p_next) {
1782278322Smav			if (port->p_auth_group == ag) {
1783278322Smav				found = true;
1784278322Smav				break;
1785278322Smav			}
1786278322Smav		}
1787273465Strasz		TAILQ_FOREACH(pg, &conf->conf_portal_groups, pg_next) {
1788273465Strasz			if (pg->pg_discovery_auth_group == ag) {
1789273465Strasz				found = true;
1790273465Strasz				break;
1791273465Strasz			}
1792273465Strasz		}
1793273465Strasz		if (!found && ag->ag_name != NULL &&
1794261762Strasz		    strcmp(ag->ag_name, "default") != 0 &&
1795255570Strasz		    strcmp(ag->ag_name, "no-authentication") != 0 &&
1796255570Strasz		    strcmp(ag->ag_name, "no-access") != 0) {
1797255570Strasz			log_warnx("auth-group \"%s\" not assigned "
1798255570Strasz			    "to any target", ag->ag_name);
1799255570Strasz		}
1800255570Strasz	}
1801255570Strasz
1802255570Strasz	return (0);
1803255570Strasz}
1804255570Strasz
1805255570Straszstatic int
1806255570Straszconf_apply(struct conf *oldconf, struct conf *newconf)
1807255570Strasz{
1808255570Strasz	struct lun *oldlun, *newlun, *tmplun;
1809255570Strasz	struct portal_group *oldpg, *newpg;
1810255570Strasz	struct portal *oldp, *newp;
1811278322Smav	struct port *oldport, *newport, *tmpport;
1812273635Smav	struct isns *oldns, *newns;
1813255570Strasz	pid_t otherpid;
1814274853Smav	int changed, cumulated_error = 0, error, sockbuf;
1815255570Strasz	int one = 1;
1816255570Strasz
1817255570Strasz	if (oldconf->conf_debug != newconf->conf_debug) {
1818255570Strasz		log_debugx("changing debug level to %d", newconf->conf_debug);
1819255570Strasz		log_init(newconf->conf_debug);
1820255570Strasz	}
1821255570Strasz
1822255570Strasz	if (oldconf->conf_pidfh != NULL) {
1823255570Strasz		assert(oldconf->conf_pidfile_path != NULL);
1824255570Strasz		if (newconf->conf_pidfile_path != NULL &&
1825255570Strasz		    strcmp(oldconf->conf_pidfile_path,
1826255570Strasz		    newconf->conf_pidfile_path) == 0) {
1827255570Strasz			newconf->conf_pidfh = oldconf->conf_pidfh;
1828255570Strasz			oldconf->conf_pidfh = NULL;
1829255570Strasz		} else {
1830255570Strasz			log_debugx("removing pidfile %s",
1831255570Strasz			    oldconf->conf_pidfile_path);
1832255570Strasz			pidfile_remove(oldconf->conf_pidfh);
1833255570Strasz			oldconf->conf_pidfh = NULL;
1834255570Strasz		}
1835255570Strasz	}
1836255570Strasz
1837255570Strasz	if (newconf->conf_pidfh == NULL && newconf->conf_pidfile_path != NULL) {
1838255570Strasz		log_debugx("opening pidfile %s", newconf->conf_pidfile_path);
1839255570Strasz		newconf->conf_pidfh =
1840255570Strasz		    pidfile_open(newconf->conf_pidfile_path, 0600, &otherpid);
1841255570Strasz		if (newconf->conf_pidfh == NULL) {
1842255570Strasz			if (errno == EEXIST)
1843255570Strasz				log_errx(1, "daemon already running, pid: %jd.",
1844255570Strasz				    (intmax_t)otherpid);
1845255570Strasz			log_err(1, "cannot open or create pidfile \"%s\"",
1846255570Strasz			    newconf->conf_pidfile_path);
1847255570Strasz		}
1848255570Strasz	}
1849255570Strasz
1850278161Smav	/*
1851278161Smav	 * Go through the new portal groups, assigning tags or preserving old.
1852278161Smav	 */
1853278161Smav	TAILQ_FOREACH(newpg, &newconf->conf_portal_groups, pg_next) {
1854287534Smav		if (newpg->pg_tag != 0)
1855287534Smav			continue;
1856278161Smav		oldpg = portal_group_find(oldconf, newpg->pg_name);
1857278161Smav		if (oldpg != NULL)
1858278161Smav			newpg->pg_tag = oldpg->pg_tag;
1859278161Smav		else
1860278161Smav			newpg->pg_tag = ++last_portal_group_tag;
1861278161Smav	}
1862278161Smav
1863273635Smav	/* Deregister on removed iSNS servers. */
1864273635Smav	TAILQ_FOREACH(oldns, &oldconf->conf_isns, i_next) {
1865273635Smav		TAILQ_FOREACH(newns, &newconf->conf_isns, i_next) {
1866273635Smav			if (strcmp(oldns->i_addr, newns->i_addr) == 0)
1867273635Smav				break;
1868273635Smav		}
1869273635Smav		if (newns == NULL)
1870273635Smav			isns_deregister(oldns);
1871273635Smav	}
1872273635Smav
1873264534Strasz	/*
1874264534Strasz	 * XXX: If target or lun removal fails, we should somehow "move"
1875273464Strasz	 *      the old lun or target into newconf, so that subsequent
1876273464Strasz	 *      conf_apply() would try to remove them again.  That would
1877273464Strasz	 *      be somewhat hairy, though, and lun deletion failures don't
1878273464Strasz	 *      really happen, so leave it as it is for now.
1879264534Strasz	 */
1880278037Smav	/*
1881278322Smav	 * First, remove any ports present in the old configuration
1882278037Smav	 * and missing in the new one.
1883278037Smav	 */
1884278322Smav	TAILQ_FOREACH_SAFE(oldport, &oldconf->conf_ports, p_next, tmpport) {
1885287534Smav		if (oldport->p_foreign)
1886287534Smav			continue;
1887278322Smav		newport = port_find(newconf, oldport->p_name);
1888287534Smav		if (newport != NULL && !newport->p_foreign)
1889278037Smav			continue;
1890278354Smav		log_debugx("removing port \"%s\"", oldport->p_name);
1891278322Smav		error = kernel_port_remove(oldport);
1892278037Smav		if (error != 0) {
1893278322Smav			log_warnx("failed to remove port %s",
1894278322Smav			    oldport->p_name);
1895278037Smav			/*
1896278037Smav			 * XXX: Uncomment after fixing the root cause.
1897278037Smav			 *
1898278037Smav			 * cumulated_error++;
1899278037Smav			 */
1900278037Smav		}
1901278037Smav	}
1902278037Smav
1903278037Smav	/*
1904278037Smav	 * Second, remove any LUNs present in the old configuration
1905278037Smav	 * and missing in the new one.
1906278037Smav	 */
1907278037Smav	TAILQ_FOREACH_SAFE(oldlun, &oldconf->conf_luns, l_next, tmplun) {
1908278037Smav		newlun = lun_find(newconf, oldlun->l_name);
1909278037Smav		if (newlun == NULL) {
1910278037Smav			log_debugx("lun \"%s\", CTL lun %d "
1911278037Smav			    "not found in new configuration; "
1912278037Smav			    "removing", oldlun->l_name, oldlun->l_ctl_lun);
1913278037Smav			error = kernel_lun_remove(oldlun);
1914274804Smav			if (error != 0) {
1915278037Smav				log_warnx("failed to remove lun \"%s\", "
1916278037Smav				    "CTL lun %d",
1917278037Smav				    oldlun->l_name, oldlun->l_ctl_lun);
1918278037Smav				cumulated_error++;
1919274804Smav			}
1920255570Strasz			continue;
1921255570Strasz		}
1922255570Strasz
1923255570Strasz		/*
1924278037Smav		 * Also remove the LUNs changed by more than size.
1925255570Strasz		 */
1926278037Smav		changed = 0;
1927278037Smav		assert(oldlun->l_backend != NULL);
1928278037Smav		assert(newlun->l_backend != NULL);
1929278037Smav		if (strcmp(newlun->l_backend, oldlun->l_backend) != 0) {
1930278037Smav			log_debugx("backend for lun \"%s\", "
1931278037Smav			    "CTL lun %d changed; removing",
1932278037Smav			    oldlun->l_name, oldlun->l_ctl_lun);
1933278037Smav			changed = 1;
1934278037Smav		}
1935278037Smav		if (oldlun->l_blocksize != newlun->l_blocksize) {
1936278037Smav			log_debugx("blocksize for lun \"%s\", "
1937278037Smav			    "CTL lun %d changed; removing",
1938278037Smav			    oldlun->l_name, oldlun->l_ctl_lun);
1939278037Smav			changed = 1;
1940278037Smav		}
1941278037Smav		if (newlun->l_device_id != NULL &&
1942278037Smav		    (oldlun->l_device_id == NULL ||
1943278037Smav		     strcmp(oldlun->l_device_id, newlun->l_device_id) !=
1944278037Smav		     0)) {
1945278037Smav			log_debugx("device-id for lun \"%s\", "
1946278037Smav			    "CTL lun %d changed; removing",
1947278037Smav			    oldlun->l_name, oldlun->l_ctl_lun);
1948278037Smav			changed = 1;
1949278037Smav		}
1950278037Smav		if (newlun->l_path != NULL &&
1951278037Smav		    (oldlun->l_path == NULL ||
1952278037Smav		     strcmp(oldlun->l_path, newlun->l_path) != 0)) {
1953278037Smav			log_debugx("path for lun \"%s\", "
1954278037Smav			    "CTL lun %d, changed; removing",
1955278037Smav			    oldlun->l_name, oldlun->l_ctl_lun);
1956278037Smav			changed = 1;
1957278037Smav		}
1958278037Smav		if (newlun->l_serial != NULL &&
1959278037Smav		    (oldlun->l_serial == NULL ||
1960278037Smav		     strcmp(oldlun->l_serial, newlun->l_serial) != 0)) {
1961278037Smav			log_debugx("serial for lun \"%s\", "
1962278037Smav			    "CTL lun %d changed; removing",
1963278037Smav			    oldlun->l_name, oldlun->l_ctl_lun);
1964278037Smav			changed = 1;
1965278037Smav		}
1966278037Smav		if (changed) {
1967278037Smav			error = kernel_lun_remove(oldlun);
1968278037Smav			if (error != 0) {
1969278037Smav				log_warnx("failed to remove lun \"%s\", "
1970278037Smav				    "CTL lun %d",
1971278037Smav				    oldlun->l_name, oldlun->l_ctl_lun);
1972278037Smav				cumulated_error++;
1973255570Strasz			}
1974278037Smav			lun_delete(oldlun);
1975278037Smav			continue;
1976278037Smav		}
1977255570Strasz
1978278037Smav		lun_set_ctl_lun(newlun, oldlun->l_ctl_lun);
1979278037Smav	}
1980278037Smav
1981278037Smav	TAILQ_FOREACH_SAFE(newlun, &newconf->conf_luns, l_next, tmplun) {
1982278037Smav		oldlun = lun_find(oldconf, newlun->l_name);
1983278037Smav		if (oldlun != NULL) {
1984287500Smav			log_debugx("modifying lun \"%s\", CTL lun %d",
1985287500Smav			    newlun->l_name, newlun->l_ctl_lun);
1986287500Smav			error = kernel_lun_modify(newlun);
1987287500Smav			if (error != 0) {
1988287500Smav				log_warnx("failed to "
1989287500Smav				    "modify lun \"%s\", CTL lun %d",
1990278037Smav				    newlun->l_name, newlun->l_ctl_lun);
1991287500Smav				cumulated_error++;
1992255570Strasz			}
1993278037Smav			continue;
1994255570Strasz		}
1995278037Smav		log_debugx("adding lun \"%s\"", newlun->l_name);
1996278037Smav		error = kernel_lun_add(newlun);
1997278037Smav		if (error != 0) {
1998278037Smav			log_warnx("failed to add lun \"%s\"", newlun->l_name);
1999278037Smav			lun_delete(newlun);
2000278037Smav			cumulated_error++;
2001278037Smav		}
2002255570Strasz	}
2003255570Strasz
2004255570Strasz	/*
2005278322Smav	 * Now add new ports or modify existing ones.
2006255570Strasz	 */
2007278322Smav	TAILQ_FOREACH(newport, &newconf->conf_ports, p_next) {
2008287534Smav		if (newport->p_foreign)
2009287534Smav			continue;
2010278322Smav		oldport = port_find(oldconf, newport->p_name);
2011255570Strasz
2012287534Smav		if (oldport == NULL || oldport->p_foreign) {
2013278354Smav			log_debugx("adding port \"%s\"", newport->p_name);
2014278322Smav			error = kernel_port_add(newport);
2015278322Smav		} else {
2016278354Smav			log_debugx("updating port \"%s\"", newport->p_name);
2017278322Smav			newport->p_ctl_port = oldport->p_ctl_port;
2018287757Smav			error = kernel_port_update(newport, oldport);
2019274791Strasz		}
2020278037Smav		if (error != 0) {
2021278322Smav			log_warnx("failed to %s port %s",
2022278322Smav			    (oldport == NULL) ? "add" : "update",
2023278322Smav			    newport->p_name);
2024278037Smav			/*
2025278037Smav			 * XXX: Uncomment after fixing the root cause.
2026278037Smav			 *
2027278037Smav			 * cumulated_error++;
2028278037Smav			 */
2029278037Smav		}
2030255570Strasz	}
2031255570Strasz
2032255570Strasz	/*
2033289677Seadler	 * Go through the new portals, opening the sockets as necessary.
2034255570Strasz	 */
2035255570Strasz	TAILQ_FOREACH(newpg, &newconf->conf_portal_groups, pg_next) {
2036287534Smav		if (newpg->pg_foreign)
2037287534Smav			continue;
2038255570Strasz		if (newpg->pg_unassigned) {
2039255570Strasz			log_debugx("not listening on portal-group \"%s\", "
2040255570Strasz			    "not assigned to any target",
2041255570Strasz			    newpg->pg_name);
2042255570Strasz			continue;
2043255570Strasz		}
2044255570Strasz		TAILQ_FOREACH(newp, &newpg->pg_portals, p_next) {
2045255570Strasz			/*
2046255570Strasz			 * Try to find already open portal and reuse
2047255570Strasz			 * the listening socket.  We don't care about
2048255570Strasz			 * what portal or portal group that was, what
2049255570Strasz			 * matters is the listening address.
2050255570Strasz			 */
2051255570Strasz			TAILQ_FOREACH(oldpg, &oldconf->conf_portal_groups,
2052255570Strasz			    pg_next) {
2053255570Strasz				TAILQ_FOREACH(oldp, &oldpg->pg_portals,
2054255570Strasz				    p_next) {
2055255570Strasz					if (strcmp(newp->p_listen,
2056255570Strasz					    oldp->p_listen) == 0 &&
2057255570Strasz					    oldp->p_socket > 0) {
2058255570Strasz						newp->p_socket =
2059255570Strasz						    oldp->p_socket;
2060255570Strasz						oldp->p_socket = 0;
2061255570Strasz						break;
2062255570Strasz					}
2063255570Strasz				}
2064255570Strasz			}
2065255570Strasz			if (newp->p_socket > 0) {
2066255570Strasz				/*
2067255570Strasz				 * We're done with this portal.
2068255570Strasz				 */
2069255570Strasz				continue;
2070255570Strasz			}
2071255570Strasz
2072255570Strasz#ifdef ICL_KERNEL_PROXY
2073264524Strasz			if (proxy_mode) {
2074264526Strasz				newpg->pg_conf->conf_portal_id++;
2075264526Strasz				newp->p_id = newpg->pg_conf->conf_portal_id;
2076264526Strasz				log_debugx("listening on %s, portal-group "
2077264526Strasz				    "\"%s\", portal id %d, using ICL proxy",
2078264526Strasz				    newp->p_listen, newpg->pg_name, newp->p_id);
2079264526Strasz				kernel_listen(newp->p_ai, newp->p_iser,
2080264526Strasz				    newp->p_id);
2081264524Strasz				continue;
2082264524Strasz			}
2083264524Strasz#endif
2084264524Strasz			assert(proxy_mode == false);
2085255570Strasz			assert(newp->p_iser == false);
2086255570Strasz
2087255570Strasz			log_debugx("listening on %s, portal-group \"%s\"",
2088255570Strasz			    newp->p_listen, newpg->pg_name);
2089255570Strasz			newp->p_socket = socket(newp->p_ai->ai_family,
2090255570Strasz			    newp->p_ai->ai_socktype,
2091255570Strasz			    newp->p_ai->ai_protocol);
2092255570Strasz			if (newp->p_socket < 0) {
2093255570Strasz				log_warn("socket(2) failed for %s",
2094255570Strasz				    newp->p_listen);
2095255570Strasz				cumulated_error++;
2096255570Strasz				continue;
2097255570Strasz			}
2098274853Smav			sockbuf = SOCKBUF_SIZE;
2099274853Smav			if (setsockopt(newp->p_socket, SOL_SOCKET, SO_RCVBUF,
2100274853Smav			    &sockbuf, sizeof(sockbuf)) == -1)
2101274853Smav				log_warn("setsockopt(SO_RCVBUF) failed "
2102274853Smav				    "for %s", newp->p_listen);
2103274853Smav			sockbuf = SOCKBUF_SIZE;
2104274853Smav			if (setsockopt(newp->p_socket, SOL_SOCKET, SO_SNDBUF,
2105274853Smav			    &sockbuf, sizeof(sockbuf)) == -1)
2106274853Smav				log_warn("setsockopt(SO_SNDBUF) failed "
2107274853Smav				    "for %s", newp->p_listen);
2108255570Strasz			error = setsockopt(newp->p_socket, SOL_SOCKET,
2109255570Strasz			    SO_REUSEADDR, &one, sizeof(one));
2110255570Strasz			if (error != 0) {
2111255570Strasz				log_warn("setsockopt(SO_REUSEADDR) failed "
2112255570Strasz				    "for %s", newp->p_listen);
2113255570Strasz				close(newp->p_socket);
2114255570Strasz				newp->p_socket = 0;
2115255570Strasz				cumulated_error++;
2116255570Strasz				continue;
2117255570Strasz			}
2118255570Strasz			error = bind(newp->p_socket, newp->p_ai->ai_addr,
2119255570Strasz			    newp->p_ai->ai_addrlen);
2120255570Strasz			if (error != 0) {
2121255570Strasz				log_warn("bind(2) failed for %s",
2122255570Strasz				    newp->p_listen);
2123255570Strasz				close(newp->p_socket);
2124255570Strasz				newp->p_socket = 0;
2125255570Strasz				cumulated_error++;
2126255570Strasz				continue;
2127255570Strasz			}
2128255570Strasz			error = listen(newp->p_socket, -1);
2129255570Strasz			if (error != 0) {
2130255570Strasz				log_warn("listen(2) failed for %s",
2131255570Strasz				    newp->p_listen);
2132255570Strasz				close(newp->p_socket);
2133255570Strasz				newp->p_socket = 0;
2134255570Strasz				cumulated_error++;
2135255570Strasz				continue;
2136255570Strasz			}
2137255570Strasz		}
2138255570Strasz	}
2139255570Strasz
2140255570Strasz	/*
2141255570Strasz	 * Go through the no longer used sockets, closing them.
2142255570Strasz	 */
2143255570Strasz	TAILQ_FOREACH(oldpg, &oldconf->conf_portal_groups, pg_next) {
2144255570Strasz		TAILQ_FOREACH(oldp, &oldpg->pg_portals, p_next) {
2145255570Strasz			if (oldp->p_socket <= 0)
2146255570Strasz				continue;
2147255570Strasz			log_debugx("closing socket for %s, portal-group \"%s\"",
2148255570Strasz			    oldp->p_listen, oldpg->pg_name);
2149255570Strasz			close(oldp->p_socket);
2150255570Strasz			oldp->p_socket = 0;
2151255570Strasz		}
2152255570Strasz	}
2153255570Strasz
2154273635Smav	/* (Re-)Register on remaining/new iSNS servers. */
2155273635Smav	TAILQ_FOREACH(newns, &newconf->conf_isns, i_next) {
2156273635Smav		TAILQ_FOREACH(oldns, &oldconf->conf_isns, i_next) {
2157273635Smav			if (strcmp(oldns->i_addr, newns->i_addr) == 0)
2158273635Smav				break;
2159273635Smav		}
2160273635Smav		isns_register(newns, oldns);
2161273635Smav	}
2162273635Smav
2163273635Smav	/* Schedule iSNS update */
2164273635Smav	if (!TAILQ_EMPTY(&newconf->conf_isns))
2165273635Smav		set_timeout((newconf->conf_isns_period + 2) / 3, false);
2166273635Smav
2167255570Strasz	return (cumulated_error);
2168255570Strasz}
2169255570Strasz
2170255570Straszbool
2171255570Strasztimed_out(void)
2172255570Strasz{
2173255570Strasz
2174255570Strasz	return (sigalrm_received);
2175255570Strasz}
2176255570Strasz
2177255570Straszstatic void
2178273635Smavsigalrm_handler_fatal(int dummy __unused)
2179255570Strasz{
2180255570Strasz	/*
2181255570Strasz	 * It would be easiest to just log an error and exit.  We can't
2182255570Strasz	 * do this, though, because log_errx() is not signal safe, since
2183255570Strasz	 * it calls syslog(3).  Instead, set a flag checked by pdu_send()
2184255570Strasz	 * and pdu_receive(), to call log_errx() there.  Should they fail
2185255570Strasz	 * to notice, we'll exit here one second later.
2186255570Strasz	 */
2187255570Strasz	if (sigalrm_received) {
2188255570Strasz		/*
2189255570Strasz		 * Oh well.  Just give up and quit.
2190255570Strasz		 */
2191255570Strasz		_exit(2);
2192255570Strasz	}
2193255570Strasz
2194255570Strasz	sigalrm_received = true;
2195255570Strasz}
2196255570Strasz
2197255570Straszstatic void
2198273635Smavsigalrm_handler(int dummy __unused)
2199255570Strasz{
2200273635Smav
2201273635Smav	sigalrm_received = true;
2202273635Smav}
2203273635Smav
2204273635Smavvoid
2205273635Smavset_timeout(int timeout, int fatal)
2206273635Smav{
2207255570Strasz	struct sigaction sa;
2208255570Strasz	struct itimerval itv;
2209255570Strasz	int error;
2210255570Strasz
2211273635Smav	if (timeout <= 0) {
2212255570Strasz		log_debugx("session timeout disabled");
2213273635Smav		bzero(&itv, sizeof(itv));
2214273635Smav		error = setitimer(ITIMER_REAL, &itv, NULL);
2215273635Smav		if (error != 0)
2216273635Smav			log_err(1, "setitimer");
2217273635Smav		sigalrm_received = false;
2218255570Strasz		return;
2219255570Strasz	}
2220255570Strasz
2221273635Smav	sigalrm_received = false;
2222255570Strasz	bzero(&sa, sizeof(sa));
2223273635Smav	if (fatal)
2224273635Smav		sa.sa_handler = sigalrm_handler_fatal;
2225273635Smav	else
2226273635Smav		sa.sa_handler = sigalrm_handler;
2227255570Strasz	sigfillset(&sa.sa_mask);
2228255570Strasz	error = sigaction(SIGALRM, &sa, NULL);
2229255570Strasz	if (error != 0)
2230255570Strasz		log_err(1, "sigaction");
2231255570Strasz
2232255570Strasz	/*
2233255570Strasz	 * First SIGALRM will arive after conf_timeout seconds.
2234255570Strasz	 * If we do nothing, another one will arrive a second later.
2235255570Strasz	 */
2236273635Smav	log_debugx("setting session timeout to %d seconds", timeout);
2237255570Strasz	bzero(&itv, sizeof(itv));
2238255570Strasz	itv.it_interval.tv_sec = 1;
2239273635Smav	itv.it_value.tv_sec = timeout;
2240255570Strasz	error = setitimer(ITIMER_REAL, &itv, NULL);
2241255570Strasz	if (error != 0)
2242255570Strasz		log_err(1, "setitimer");
2243255570Strasz}
2244255570Strasz
2245255570Straszstatic int
2246255570Straszwait_for_children(bool block)
2247255570Strasz{
2248255570Strasz	pid_t pid;
2249255570Strasz	int status;
2250255570Strasz	int num = 0;
2251255570Strasz
2252255570Strasz	for (;;) {
2253255570Strasz		/*
2254255570Strasz		 * If "block" is true, wait for at least one process.
2255255570Strasz		 */
2256255570Strasz		if (block && num == 0)
2257255570Strasz			pid = wait4(-1, &status, 0, NULL);
2258255570Strasz		else
2259255570Strasz			pid = wait4(-1, &status, WNOHANG, NULL);
2260255570Strasz		if (pid <= 0)
2261255570Strasz			break;
2262255570Strasz		if (WIFSIGNALED(status)) {
2263255570Strasz			log_warnx("child process %d terminated with signal %d",
2264255570Strasz			    pid, WTERMSIG(status));
2265255570Strasz		} else if (WEXITSTATUS(status) != 0) {
2266255570Strasz			log_warnx("child process %d terminated with exit status %d",
2267255570Strasz			    pid, WEXITSTATUS(status));
2268255570Strasz		} else {
2269255570Strasz			log_debugx("child process %d terminated gracefully", pid);
2270255570Strasz		}
2271255570Strasz		num++;
2272255570Strasz	}
2273255570Strasz
2274255570Strasz	return (num);
2275255570Strasz}
2276255570Strasz
2277255570Straszstatic void
2278264530Straszhandle_connection(struct portal *portal, int fd,
2279269183Smav    const struct sockaddr *client_sa, bool dont_fork)
2280255570Strasz{
2281255570Strasz	struct connection *conn;
2282255570Strasz	int error;
2283255570Strasz	pid_t pid;
2284255570Strasz	char host[NI_MAXHOST + 1];
2285255570Strasz	struct conf *conf;
2286255570Strasz
2287255570Strasz	conf = portal->p_portal_group->pg_conf;
2288255570Strasz
2289255570Strasz	if (dont_fork) {
2290255570Strasz		log_debugx("incoming connection; not forking due to -d flag");
2291255570Strasz	} else {
2292255570Strasz		nchildren -= wait_for_children(false);
2293255570Strasz		assert(nchildren >= 0);
2294255570Strasz
2295255570Strasz		while (conf->conf_maxproc > 0 && nchildren >= conf->conf_maxproc) {
2296255570Strasz			log_debugx("maxproc limit of %d child processes hit; "
2297255570Strasz			    "waiting for child process to exit", conf->conf_maxproc);
2298255570Strasz			nchildren -= wait_for_children(true);
2299255570Strasz			assert(nchildren >= 0);
2300255570Strasz		}
2301255570Strasz		log_debugx("incoming connection; forking child process #%d",
2302255570Strasz		    nchildren);
2303255570Strasz		nchildren++;
2304255570Strasz		pid = fork();
2305255570Strasz		if (pid < 0)
2306255570Strasz			log_err(1, "fork");
2307255570Strasz		if (pid > 0) {
2308255570Strasz			close(fd);
2309255570Strasz			return;
2310255570Strasz		}
2311255570Strasz	}
2312255570Strasz	pidfile_close(conf->conf_pidfh);
2313255570Strasz
2314269183Smav	error = getnameinfo(client_sa, client_sa->sa_len,
2315264530Strasz	    host, sizeof(host), NULL, 0, NI_NUMERICHOST);
2316264530Strasz	if (error != 0)
2317264530Strasz		log_errx(1, "getnameinfo: %s", gai_strerror(error));
2318255570Strasz
2319264530Strasz	log_debugx("accepted connection from %s; portal group \"%s\"",
2320264530Strasz	    host, portal->p_portal_group->pg_name);
2321264530Strasz	log_set_peer_addr(host);
2322264530Strasz	setproctitle("%s", host);
2323255570Strasz
2324269183Smav	conn = connection_new(portal, fd, host, client_sa);
2325273635Smav	set_timeout(conf->conf_timeout, true);
2326255570Strasz	kernel_capsicate();
2327255570Strasz	login(conn);
2328255570Strasz	if (conn->conn_session_type == CONN_SESSION_TYPE_NORMAL) {
2329255570Strasz		kernel_handoff(conn);
2330255570Strasz		log_debugx("connection handed off to the kernel");
2331255570Strasz	} else {
2332255570Strasz		assert(conn->conn_session_type == CONN_SESSION_TYPE_DISCOVERY);
2333255570Strasz		discovery(conn);
2334255570Strasz	}
2335255570Strasz	log_debugx("nothing more to do; exiting");
2336255570Strasz	exit(0);
2337255570Strasz}
2338255570Strasz
2339255570Straszstatic int
2340255570Straszfd_add(int fd, fd_set *fdset, int nfds)
2341255570Strasz{
2342255570Strasz
2343255570Strasz	/*
2344255570Strasz	 * Skip sockets which we failed to bind.
2345255570Strasz	 */
2346255570Strasz	if (fd <= 0)
2347255570Strasz		return (nfds);
2348255570Strasz
2349255570Strasz	FD_SET(fd, fdset);
2350255570Strasz	if (fd > nfds)
2351255570Strasz		nfds = fd;
2352255570Strasz	return (nfds);
2353255570Strasz}
2354255570Strasz
2355255570Straszstatic void
2356255570Straszmain_loop(struct conf *conf, bool dont_fork)
2357255570Strasz{
2358255570Strasz	struct portal_group *pg;
2359255570Strasz	struct portal *portal;
2360264529Strasz	struct sockaddr_storage client_sa;
2361264529Strasz	socklen_t client_salen;
2362255570Strasz#ifdef ICL_KERNEL_PROXY
2363255570Strasz	int connection_id;
2364264526Strasz	int portal_id;
2365264524Strasz#endif
2366255570Strasz	fd_set fdset;
2367255570Strasz	int error, nfds, client_fd;
2368255570Strasz
2369255570Strasz	pidfile_write(conf->conf_pidfh);
2370255570Strasz
2371255570Strasz	for (;;) {
2372273635Smav		if (sighup_received || sigterm_received || timed_out())
2373255570Strasz			return;
2374255570Strasz
2375255570Strasz#ifdef ICL_KERNEL_PROXY
2376264524Strasz		if (proxy_mode) {
2377264530Strasz			client_salen = sizeof(client_sa);
2378264530Strasz			kernel_accept(&connection_id, &portal_id,
2379264530Strasz			    (struct sockaddr *)&client_sa, &client_salen);
2380271169Strasz			assert(client_salen >= client_sa.ss_len);
2381255570Strasz
2382264526Strasz			log_debugx("incoming connection, id %d, portal id %d",
2383264526Strasz			    connection_id, portal_id);
2384264526Strasz			TAILQ_FOREACH(pg, &conf->conf_portal_groups, pg_next) {
2385264526Strasz				TAILQ_FOREACH(portal, &pg->pg_portals, p_next) {
2386264526Strasz					if (portal->p_id == portal_id) {
2387264526Strasz						goto found;
2388264526Strasz					}
2389264526Strasz				}
2390264526Strasz			}
2391255570Strasz
2392264526Strasz			log_errx(1, "kernel returned invalid portal_id %d",
2393264526Strasz			    portal_id);
2394264526Strasz
2395264526Straszfound:
2396264530Strasz			handle_connection(portal, connection_id,
2397269183Smav			    (struct sockaddr *)&client_sa, dont_fork);
2398264524Strasz		} else {
2399264524Strasz#endif
2400264524Strasz			assert(proxy_mode == false);
2401264524Strasz
2402264524Strasz			FD_ZERO(&fdset);
2403264524Strasz			nfds = 0;
2404264524Strasz			TAILQ_FOREACH(pg, &conf->conf_portal_groups, pg_next) {
2405264524Strasz				TAILQ_FOREACH(portal, &pg->pg_portals, p_next)
2406264524Strasz					nfds = fd_add(portal->p_socket, &fdset, nfds);
2407255570Strasz			}
2408264524Strasz			error = select(nfds + 1, &fdset, NULL, NULL, NULL);
2409264524Strasz			if (error <= 0) {
2410264524Strasz				if (errno == EINTR)
2411264524Strasz					return;
2412264524Strasz				log_err(1, "select");
2413264524Strasz			}
2414264524Strasz			TAILQ_FOREACH(pg, &conf->conf_portal_groups, pg_next) {
2415264524Strasz				TAILQ_FOREACH(portal, &pg->pg_portals, p_next) {
2416264524Strasz					if (!FD_ISSET(portal->p_socket, &fdset))
2417264524Strasz						continue;
2418264529Strasz					client_salen = sizeof(client_sa);
2419264529Strasz					client_fd = accept(portal->p_socket,
2420264529Strasz					    (struct sockaddr *)&client_sa,
2421264529Strasz					    &client_salen);
2422281163Smav					if (client_fd < 0) {
2423281163Smav						if (errno == ECONNABORTED)
2424281163Smav							continue;
2425264524Strasz						log_err(1, "accept");
2426281163Smav					}
2427271169Strasz					assert(client_salen >= client_sa.ss_len);
2428271169Strasz
2429264529Strasz					handle_connection(portal, client_fd,
2430264530Strasz					    (struct sockaddr *)&client_sa,
2431269183Smav					    dont_fork);
2432264524Strasz					break;
2433264524Strasz				}
2434264524Strasz			}
2435264524Strasz#ifdef ICL_KERNEL_PROXY
2436255570Strasz		}
2437264524Strasz#endif
2438255570Strasz	}
2439255570Strasz}
2440255570Strasz
2441255570Straszstatic void
2442255570Straszsighup_handler(int dummy __unused)
2443255570Strasz{
2444255570Strasz
2445255570Strasz	sighup_received = true;
2446255570Strasz}
2447255570Strasz
2448255570Straszstatic void
2449255570Straszsigterm_handler(int dummy __unused)
2450255570Strasz{
2451255570Strasz
2452255570Strasz	sigterm_received = true;
2453255570Strasz}
2454255570Strasz
2455255570Straszstatic void
2456261764Straszsigchld_handler(int dummy __unused)
2457261764Strasz{
2458261764Strasz
2459261764Strasz	/*
2460261764Strasz	 * The only purpose of this handler is to make SIGCHLD
2461261764Strasz	 * interrupt the ISCSIDWAIT ioctl(2), so we can call
2462261764Strasz	 * wait_for_children().
2463261764Strasz	 */
2464261764Strasz}
2465261764Strasz
2466261764Straszstatic void
2467255570Straszregister_signals(void)
2468255570Strasz{
2469255570Strasz	struct sigaction sa;
2470255570Strasz	int error;
2471255570Strasz
2472255570Strasz	bzero(&sa, sizeof(sa));
2473255570Strasz	sa.sa_handler = sighup_handler;
2474255570Strasz	sigfillset(&sa.sa_mask);
2475255570Strasz	error = sigaction(SIGHUP, &sa, NULL);
2476255570Strasz	if (error != 0)
2477255570Strasz		log_err(1, "sigaction");
2478255570Strasz
2479255570Strasz	sa.sa_handler = sigterm_handler;
2480255570Strasz	error = sigaction(SIGTERM, &sa, NULL);
2481255570Strasz	if (error != 0)
2482255570Strasz		log_err(1, "sigaction");
2483255570Strasz
2484255570Strasz	sa.sa_handler = sigterm_handler;
2485255570Strasz	error = sigaction(SIGINT, &sa, NULL);
2486255570Strasz	if (error != 0)
2487255570Strasz		log_err(1, "sigaction");
2488261764Strasz
2489261764Strasz	sa.sa_handler = sigchld_handler;
2490261764Strasz	error = sigaction(SIGCHLD, &sa, NULL);
2491261764Strasz	if (error != 0)
2492261764Strasz		log_err(1, "sigaction");
2493255570Strasz}
2494255570Strasz
2495295212Sjceelstatic void
2496295212Sjceelcheck_perms(const char *path)
2497295212Sjceel{
2498295212Sjceel	struct stat sb;
2499295212Sjceel	int error;
2500295212Sjceel
2501295212Sjceel	error = stat(path, &sb);
2502295212Sjceel	if (error != 0) {
2503295212Sjceel		log_warn("stat");
2504295212Sjceel		return;
2505295212Sjceel	}
2506295212Sjceel	if (sb.st_mode & S_IWOTH) {
2507295212Sjceel		log_warnx("%s is world-writable", path);
2508295212Sjceel	} else if (sb.st_mode & S_IROTH) {
2509295212Sjceel		log_warnx("%s is world-readable", path);
2510295212Sjceel	} else if (sb.st_mode & S_IXOTH) {
2511295212Sjceel		/*
2512295212Sjceel		 * Ok, this one doesn't matter, but still do it,
2513295212Sjceel		 * just for consistency.
2514295212Sjceel		 */
2515295212Sjceel		log_warnx("%s is world-executable", path);
2516295212Sjceel	}
2517295212Sjceel
2518295212Sjceel	/*
2519295212Sjceel	 * XXX: Should we also check for owner != 0?
2520295212Sjceel	 */
2521295212Sjceel}
2522295212Sjceel
2523295212Sjceelstatic struct conf *
2524295212Sjceelconf_new_from_file(const char *path, struct conf *oldconf, bool ucl)
2525295212Sjceel{
2526295212Sjceel	struct conf *conf;
2527295212Sjceel	struct auth_group *ag;
2528295212Sjceel	struct portal_group *pg;
2529295212Sjceel	struct pport *pp;
2530295212Sjceel	int error;
2531295212Sjceel
2532295212Sjceel	log_debugx("obtaining configuration from %s", path);
2533295212Sjceel
2534295212Sjceel	conf = conf_new();
2535295212Sjceel
2536295212Sjceel	TAILQ_FOREACH(pp, &oldconf->conf_pports, pp_next)
2537295212Sjceel		pport_copy(pp, conf);
2538295212Sjceel
2539295212Sjceel	ag = auth_group_new(conf, "default");
2540295212Sjceel	assert(ag != NULL);
2541295212Sjceel
2542295212Sjceel	ag = auth_group_new(conf, "no-authentication");
2543295212Sjceel	assert(ag != NULL);
2544295212Sjceel	ag->ag_type = AG_TYPE_NO_AUTHENTICATION;
2545295212Sjceel
2546295212Sjceel	ag = auth_group_new(conf, "no-access");
2547295212Sjceel	assert(ag != NULL);
2548295212Sjceel	ag->ag_type = AG_TYPE_DENY;
2549295212Sjceel
2550295212Sjceel	pg = portal_group_new(conf, "default");
2551295212Sjceel	assert(pg != NULL);
2552295212Sjceel
2553295212Sjceel	if (ucl)
2554295212Sjceel		error = uclparse_conf(conf, path);
2555295212Sjceel	else
2556295212Sjceel		error = parse_conf(conf, path);
2557295212Sjceel
2558295212Sjceel	if (error != 0) {
2559295212Sjceel		conf_delete(conf);
2560295212Sjceel		return (NULL);
2561295212Sjceel	}
2562295212Sjceel
2563295212Sjceel	check_perms(path);
2564295212Sjceel
2565295212Sjceel	if (conf->conf_default_ag_defined == false) {
2566295212Sjceel		log_debugx("auth-group \"default\" not defined; "
2567295212Sjceel		    "going with defaults");
2568295212Sjceel		ag = auth_group_find(conf, "default");
2569295212Sjceel		assert(ag != NULL);
2570295212Sjceel		ag->ag_type = AG_TYPE_DENY;
2571295212Sjceel	}
2572295212Sjceel
2573295212Sjceel	if (conf->conf_default_pg_defined == false) {
2574295212Sjceel		log_debugx("portal-group \"default\" not defined; "
2575295212Sjceel		    "going with defaults");
2576295212Sjceel		pg = portal_group_find(conf, "default");
2577295212Sjceel		assert(pg != NULL);
2578295212Sjceel		portal_group_add_listen(pg, "0.0.0.0:3260", false);
2579295212Sjceel		portal_group_add_listen(pg, "[::]:3260", false);
2580295212Sjceel	}
2581295212Sjceel
2582295212Sjceel	conf->conf_kernel_port_on = true;
2583295212Sjceel
2584295212Sjceel	error = conf_verify(conf);
2585295212Sjceel	if (error != 0) {
2586295212Sjceel		conf_delete(conf);
2587295212Sjceel		return (NULL);
2588295212Sjceel	}
2589295212Sjceel
2590295212Sjceel	return (conf);
2591295212Sjceel}
2592295212Sjceel
2593255570Straszint
2594255570Straszmain(int argc, char **argv)
2595255570Strasz{
2596255570Strasz	struct conf *oldconf, *newconf, *tmpconf;
2597273635Smav	struct isns *newns;
2598255570Strasz	const char *config_path = DEFAULT_CONFIG_PATH;
2599255570Strasz	int debug = 0, ch, error;
2600255570Strasz	bool dont_daemonize = false;
2601295212Sjceel	bool use_ucl = false;
2602255570Strasz
2603295212Sjceel	while ((ch = getopt(argc, argv, "duf:R")) != -1) {
2604255570Strasz		switch (ch) {
2605255570Strasz		case 'd':
2606255570Strasz			dont_daemonize = true;
2607255570Strasz			debug++;
2608255570Strasz			break;
2609295212Sjceel		case 'u':
2610295212Sjceel			use_ucl = true;
2611295212Sjceel			break;
2612255570Strasz		case 'f':
2613255570Strasz			config_path = optarg;
2614255570Strasz			break;
2615264524Strasz		case 'R':
2616264524Strasz#ifndef ICL_KERNEL_PROXY
2617264524Strasz			log_errx(1, "ctld(8) compiled without ICL_KERNEL_PROXY "
2618264524Strasz			    "does not support iSER protocol");
2619264524Strasz#endif
2620264524Strasz			proxy_mode = true;
2621264524Strasz			break;
2622255570Strasz		case '?':
2623255570Strasz		default:
2624255570Strasz			usage();
2625255570Strasz		}
2626255570Strasz	}
2627255570Strasz	argc -= optind;
2628255570Strasz	if (argc != 0)
2629255570Strasz		usage();
2630255570Strasz
2631255570Strasz	log_init(debug);
2632255570Strasz	kernel_init();
2633255570Strasz
2634255570Strasz	oldconf = conf_new_from_kernel();
2635295212Sjceel	newconf = conf_new_from_file(config_path, oldconf, use_ucl);
2636295212Sjceel
2637255570Strasz	if (newconf == NULL)
2638264533Strasz		log_errx(1, "configuration error; exiting");
2639255570Strasz	if (debug > 0) {
2640255570Strasz		oldconf->conf_debug = debug;
2641255570Strasz		newconf->conf_debug = debug;
2642255570Strasz	}
2643255570Strasz
2644255570Strasz	error = conf_apply(oldconf, newconf);
2645255570Strasz	if (error != 0)
2646264533Strasz		log_errx(1, "failed to apply configuration; exiting");
2647264533Strasz
2648255570Strasz	conf_delete(oldconf);
2649255570Strasz	oldconf = NULL;
2650255570Strasz
2651255570Strasz	register_signals();
2652255570Strasz
2653261753Strasz	if (dont_daemonize == false) {
2654261753Strasz		log_debugx("daemonizing");
2655261753Strasz		if (daemon(0, 0) == -1) {
2656261753Strasz			log_warn("cannot daemonize");
2657261753Strasz			pidfile_remove(newconf->conf_pidfh);
2658261753Strasz			exit(1);
2659261753Strasz		}
2660261753Strasz	}
2661261753Strasz
2662273635Smav	/* Schedule iSNS update */
2663273635Smav	if (!TAILQ_EMPTY(&newconf->conf_isns))
2664273635Smav		set_timeout((newconf->conf_isns_period + 2) / 3, false);
2665273635Smav
2666255570Strasz	for (;;) {
2667255570Strasz		main_loop(newconf, dont_daemonize);
2668255570Strasz		if (sighup_received) {
2669255570Strasz			sighup_received = false;
2670255570Strasz			log_debugx("received SIGHUP, reloading configuration");
2671295212Sjceel			tmpconf = conf_new_from_file(config_path, newconf,
2672295212Sjceel			    use_ucl);
2673295212Sjceel
2674255570Strasz			if (tmpconf == NULL) {
2675255570Strasz				log_warnx("configuration error, "
2676255570Strasz				    "continuing with old configuration");
2677255570Strasz			} else {
2678255570Strasz				if (debug > 0)
2679255570Strasz					tmpconf->conf_debug = debug;
2680255570Strasz				oldconf = newconf;
2681255570Strasz				newconf = tmpconf;
2682255570Strasz				error = conf_apply(oldconf, newconf);
2683255570Strasz				if (error != 0)
2684255570Strasz					log_warnx("failed to reload "
2685255570Strasz					    "configuration");
2686255570Strasz				conf_delete(oldconf);
2687255570Strasz				oldconf = NULL;
2688255570Strasz			}
2689255570Strasz		} else if (sigterm_received) {
2690255570Strasz			log_debugx("exiting on signal; "
2691255570Strasz			    "reloading empty configuration");
2692255570Strasz
2693278161Smav			log_debugx("removing CTL iSCSI ports "
2694255570Strasz			    "and terminating all connections");
2695255570Strasz
2696255570Strasz			oldconf = newconf;
2697255570Strasz			newconf = conf_new();
2698255570Strasz			if (debug > 0)
2699255570Strasz				newconf->conf_debug = debug;
2700255570Strasz			error = conf_apply(oldconf, newconf);
2701255570Strasz			if (error != 0)
2702255570Strasz				log_warnx("failed to apply configuration");
2703273635Smav			conf_delete(oldconf);
2704273635Smav			oldconf = NULL;
2705255570Strasz
2706255570Strasz			log_warnx("exiting on signal");
2707255570Strasz			exit(0);
2708255570Strasz		} else {
2709255570Strasz			nchildren -= wait_for_children(false);
2710255570Strasz			assert(nchildren >= 0);
2711273635Smav			if (timed_out()) {
2712273635Smav				set_timeout(0, false);
2713273635Smav				TAILQ_FOREACH(newns, &newconf->conf_isns, i_next)
2714273635Smav					isns_check(newns);
2715273635Smav				/* Schedule iSNS update */
2716273635Smav				if (!TAILQ_EMPTY(&newconf->conf_isns)) {
2717273635Smav					set_timeout((newconf->conf_isns_period
2718273635Smav					    + 2) / 3,
2719273635Smav					    false);
2720273635Smav				}
2721273635Smav			}
2722255570Strasz		}
2723255570Strasz	}
2724255570Strasz	/* NOTREACHED */
2725255570Strasz}
2726