xref: /openbsd/usr.sbin/iscsid/iscsid.c (revision 91f110e0)
1 /*	$OpenBSD: iscsid.c,v 1.9 2014/02/17 18:59:50 claudio Exp $ */
2 
3 /*
4  * Copyright (c) 2009 Claudio Jeker <claudio@openbsd.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/types.h>
20 #include <sys/queue.h>
21 #include <sys/socket.h>
22 #include <sys/time.h>
23 #include <sys/uio.h>
24 
25 #include <err.h>
26 #include <event.h>
27 #include <pwd.h>
28 #include <signal.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <unistd.h>
33 
34 #include "iscsid.h"
35 #include "log.h"
36 
37 void		main_sig_handler(int, short, void *);
38 __dead void	usage(void);
39 void		shutdown_cb(int, short, void *);
40 
41 struct initiator *initiator;
42 struct event exit_ev;
43 int exit_rounds;
44 #define ISCSI_EXIT_WAIT 5
45 
46 const struct session_params	iscsi_sess_defaults = {
47 	.MaxBurstLength = 262144,
48 	.FirstBurstLength = 65536,
49 	.DefaultTime2Wait = 2,
50 	.DefaultTime2Retain = 20,
51 	.MaxOutstandingR2T = 1,
52 	.MaxConnections = 1,
53 	.InitialR2T = 1,
54 	.ImmediateData = 1,
55 	.DataPDUInOrder = 1,
56 	.DataSequenceInOrder = 1,
57 	.ErrorRecoveryLevel = 0
58 };
59 
60 const struct connection_params	iscsi_conn_defaults = {
61 	.MaxRecvDataSegmentLength = 8192
62 };
63 
64 int
65 main(int argc, char *argv[])
66 {
67 	struct event ev_sigint, ev_sigterm, ev_sighup;
68 	struct passwd *pw;
69 	char *ctrlsock = ISCSID_CONTROL;
70 	char *vscsidev = ISCSID_DEVICE;
71 	int ch, debug = 0, verbose = 0;
72 
73 	log_init(1);    /* log to stderr until daemonized */
74 	log_verbose(1);
75 
76 	while ((ch = getopt(argc, argv, "dn:s:v")) != -1) {
77 		switch (ch) {
78 		case 'd':
79 			debug = 1;
80 			break;
81 		case 'n':
82 			vscsidev = optarg;
83 			break;
84 		case 's':
85 			ctrlsock = optarg;
86 			break;
87 		case 'v':
88 			verbose = 1;
89 			break;
90 		default:
91 			usage();
92 			/* NOTREACHED */
93 		}
94 	}
95 
96 	argc -= optind;
97 	argv += optind;
98 
99 	if (argc > 0)
100 		usage();
101 
102 	/* check for root privileges  */
103 	if (geteuid())
104 		errx(1, "need root privileges");
105 
106 	log_init(debug);
107 	log_verbose(verbose);
108 
109 	if (control_init(ctrlsock) == -1)
110 		fatalx("control socket setup failed");
111 
112 	if (!debug)
113 		daemon(1, 0);
114 	log_info("startup");
115 
116 	event_init();
117 	vscsi_open(vscsidev);
118 
119 	/* chroot and drop to iscsid user */
120 	if ((pw = getpwnam(ISCSID_USER)) == NULL)
121 		errx(1, "unknown user %s", ISCSID_USER);
122 
123 	if (chroot(pw->pw_dir) == -1)
124 		fatal("chroot");
125 	if (chdir("/") == -1)
126 		fatal("chdir(\"/\")");
127 	if (setgroups(1, &pw->pw_gid) ||
128 	    setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
129 	    setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
130 		fatal("can't drop privileges");
131 
132 	/* setup signal handler */
133 	signal_set(&ev_sigint, SIGINT, main_sig_handler, NULL);
134 	signal_set(&ev_sigterm, SIGTERM, main_sig_handler, NULL);
135 	signal_set(&ev_sighup, SIGHUP, main_sig_handler, NULL);
136 	signal_add(&ev_sigint, NULL);
137 	signal_add(&ev_sigterm, NULL);
138 	signal_add(&ev_sighup, NULL);
139 	signal(SIGPIPE, SIG_IGN);
140 
141 	if (control_listen() == -1)
142 		fatalx("control socket listen failed");
143 
144 	initiator = initiator_init();
145 
146 	event_dispatch();
147 
148 	/* CLEANUP XXX */
149 	control_cleanup(ctrlsock);
150 	initiator_cleanup(initiator);
151 	log_info("exiting.");
152 	return 0;
153 }
154 
155 /* ARGSUSED */
156 void
157 main_sig_handler(int sig, short event, void *arg)
158 {
159 	struct timeval tv;
160 
161 	/* signal handler rules don't apply, libevent decouples for us */
162 	switch (sig) {
163 	case SIGTERM:
164 	case SIGINT:
165 	case SIGHUP:
166 		initiator_shutdown(initiator);
167 		evtimer_set(&exit_ev, shutdown_cb, NULL);
168 		timerclear(&tv);
169 		if (evtimer_add(&exit_ev, &tv) == -1)
170 			fatal("main_sig_handler");
171 		break;
172 	default:
173 		fatalx("unexpected signal");
174 		/* NOTREACHED */
175 	}
176 }
177 
178 __dead void
179 usage(void)
180 {
181 	extern char *__progname;
182 
183 	fprintf(stderr, "usage: %s [-dv] [-n device] [-s socket]\n",
184 	    __progname);
185 	exit(1);
186 }
187 
188 void
189 iscsid_ctrl_dispatch(void *ch, struct pdu *pdu)
190 {
191 	struct ctrlmsghdr *cmh;
192 	struct initiator_config *ic;
193 	struct session_config *sc;
194 	struct session *s;
195 	int *valp;
196 
197 	cmh = pdu_getbuf(pdu, NULL, 0);
198 	if (cmh == NULL)
199 		goto done;
200 
201 	switch (cmh->type) {
202 	case CTRL_INITIATOR_CONFIG:
203 		if (cmh->len[0] != sizeof(*ic)) {
204 			log_warnx("CTRL_INITIATOR_CONFIG bad size");
205 			control_compose(ch, CTRL_FAILURE, NULL, 0);
206 			break;
207 		}
208 		ic = pdu_getbuf(pdu, NULL, 1);
209 		bcopy(ic, &initiator->config, sizeof(initiator->config));
210 		control_compose(ch, CTRL_SUCCESS, NULL, 0);
211 		break;
212 	case CTRL_SESSION_CONFIG:
213 		if (cmh->len[0] != sizeof(*sc)) {
214 			log_warnx("CTRL_SESSION_CONFIG bad size");
215 			control_compose(ch, CTRL_FAILURE, NULL, 0);
216 			break;
217 		}
218 		sc = pdu_getbuf(pdu, NULL, 1);
219 		if (cmh->len[1])
220 			sc->TargetName = pdu_getbuf(pdu, NULL, 2);
221 		else if (sc->SessionType != SESSION_TYPE_DISCOVERY) {
222 			control_compose(ch, CTRL_FAILURE, NULL, 0);
223 			goto done;
224 		} else
225 			sc->TargetName = NULL;
226 		if (cmh->len[2])
227 			sc->InitiatorName = pdu_getbuf(pdu, NULL, 3);
228 		else
229 			sc->InitiatorName = NULL;
230 
231 		s = session_find(initiator, sc->SessionName);
232 		if (s == NULL) {
233 			s = session_new(initiator, sc->SessionType);
234 			if (s == NULL) {
235 				control_compose(ch, CTRL_FAILURE, NULL, 0);
236 				goto done;
237 			}
238 		}
239 
240 		session_config(s, sc);
241 		if (s->state == SESS_INIT)
242 			session_fsm(s, SESS_EV_START, NULL);
243 
244 		control_compose(ch, CTRL_SUCCESS, NULL, 0);
245 		break;
246 	case CTRL_LOG_VERBOSE:
247 		if (cmh->len[0] != sizeof(int)) {
248 			log_warnx("CTRL_LOG_VERBOSE bad size");
249 			control_compose(ch, CTRL_FAILURE, NULL, 0);
250 			break;
251 		}
252 		valp = pdu_getbuf(pdu, NULL, 1);
253 		log_verbose(*valp);
254 		control_compose(ch, CTRL_SUCCESS, NULL, 0);
255 		break;
256 	default:
257 		log_warnx("unknown control message type %d", cmh->type);
258 		control_compose(ch, CTRL_FAILURE, NULL, 0);
259 		break;
260 	}
261 
262 done:
263 	pdu_free(pdu);
264 }
265 
266 void
267 shutdown_cb(int fd, short event, void *arg)
268 {
269 	struct timeval tv;
270 
271 	if (exit_rounds++ >= ISCSI_EXIT_WAIT || initiator_isdown(initiator))
272 		event_loopexit(NULL);
273 
274 	timerclear(&tv);
275 	tv.tv_sec = 1;
276 
277 	if (evtimer_add(&exit_ev, &tv) == -1)
278 		fatal("shutdown_cb");
279 }
280 
281 #define MERGE_MIN(r, a, b, v)				\
282 	res->v = (mine->v < his->v ? mine->v : his->v)
283 #define MERGE_MAX(r, a, b, v)				\
284 	res->v = (mine->v > his->v ? mine->v : his->v)
285 #define MERGE_OR(r, a, b, v)				\
286 	res->v = (mine->v || his->v)
287 #define MERGE_AND(r, a, b, v)				\
288 	res->v = (mine->v && his->v)
289 
290 void
291 iscsi_merge_sess_params(struct session_params *res,
292     struct session_params *mine, struct session_params *his)
293 {
294 	MERGE_MIN(res, mine, his, MaxBurstLength);
295 	MERGE_MIN(res, mine, his, FirstBurstLength);
296 	MERGE_MAX(res, mine, his, DefaultTime2Wait);
297 	MERGE_MIN(res, mine, his, DefaultTime2Retain);
298 	MERGE_MIN(res, mine, his, MaxOutstandingR2T);
299 	res->TargetPortalGroupTag = his->TargetPortalGroupTag;
300 	MERGE_MIN(res, mine, his, MaxConnections);
301 	MERGE_OR(res, mine, his, InitialR2T);
302 	MERGE_AND(res, mine, his, ImmediateData);
303 	MERGE_OR(res, mine, his, DataPDUInOrder);
304 	MERGE_OR(res, mine, his, DataSequenceInOrder);
305 	MERGE_MIN(res, mine, his, ErrorRecoveryLevel);
306 
307 }
308 
309 void
310 iscsi_merge_conn_params(struct connection_params *res,
311     struct connection_params *mine, struct connection_params *his)
312 {
313 	res->MaxRecvDataSegmentLength = his->MaxRecvDataSegmentLength;
314 	/* XXX HeaderDigest and DataDigest */
315 }
316 
317 #undef MERGE_MIN
318 #undef MERGE_MAX
319 #undef MERGE_OR
320 #undef MERGE_AND
321