]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/iscsid/iscsid.c
ntp: import ntp-4.2.8p16
[FreeBSD/FreeBSD.git] / usr.sbin / iscsid / iscsid.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2012 The FreeBSD Foundation
5  *
6  * This software was developed by Edward Tomasz Napierala under sponsorship
7  * from the FreeBSD Foundation.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  *
30  */
31
32 #include <sys/cdefs.h>
33 __FBSDID("$FreeBSD$");
34
35 #include <sys/types.h>
36 #include <sys/time.h>
37 #include <sys/ioctl.h>
38 #include <sys/param.h>
39 #include <sys/linker.h>
40 #include <sys/socket.h>
41 #include <sys/sysctl.h>
42 #include <sys/capsicum.h>
43 #include <sys/wait.h>
44 #include <netinet/in.h>
45 #include <netinet/tcp.h>
46 #include <assert.h>
47 #include <capsicum_helpers.h>
48 #include <errno.h>
49 #include <fcntl.h>
50 #include <libutil.h>
51 #include <netdb.h>
52 #include <signal.h>
53 #include <stdbool.h>
54 #include <stdint.h>
55 #include <stdio.h>
56 #include <stdlib.h>
57 #include <string.h>
58 #include <unistd.h>
59
60 #include "iscsid.h"
61
62 static bool     timed_out(void);
63 #ifdef ICL_KERNEL_PROXY
64 static void     pdu_receive_proxy(struct pdu *pdu);
65 static void     pdu_send_proxy(struct pdu *pdu);
66 #endif /* ICL_KERNEL_PROXY */
67
68 static volatile bool sigalrm_received = false;
69
70 static int nchildren = 0;
71
72 static struct connection_ops conn_ops = {
73         .timed_out = timed_out,
74 #ifdef ICL_KERNEL_PROXY
75         .pdu_receive_proxy = pdu_receive_proxy,
76         .pdu_send_proxy = pdu_send_proxy,
77 #endif
78         .fail = fail,
79 };
80
81 static void
82 usage(void)
83 {
84
85         fprintf(stderr, "usage: iscsid [-P pidfile][-d][-m maxproc][-t timeout]\n");
86         exit(1);
87 }
88
89 #ifdef ICL_KERNEL_PROXY
90
91 static void
92 pdu_receive_proxy(struct pdu *pdu)
93 {
94         struct iscsid_connection *conn;
95         struct iscsi_daemon_receive idr;
96         size_t len;
97         int error;
98
99         conn = (struct iscsid_connection *)pdu->pdu_connection;
100         assert(conn->conn_conf.isc_iser != 0);
101
102         pdu->pdu_data = malloc(conn->conn.conn_max_recv_data_segment_length);
103         if (pdu->pdu_data == NULL)
104                 log_err(1, "malloc");
105
106         memset(&idr, 0, sizeof(idr));
107         idr.idr_session_id = conn->conn_session_id;
108         idr.idr_bhs = pdu->pdu_bhs;
109         idr.idr_data_segment_len = conn->conn.conn_max_recv_data_segment_length;
110         idr.idr_data_segment = pdu->pdu_data;
111
112         error = ioctl(conn->conn_iscsi_fd, ISCSIDRECEIVE, &idr);
113         if (error != 0)
114                 log_err(1, "ISCSIDRECEIVE");
115
116         len = pdu_ahs_length(pdu);
117         if (len > 0)
118                 log_errx(1, "protocol error: non-empty AHS");
119
120         len = pdu_data_segment_length(pdu);
121         assert(len <= (size_t)conn->conn.conn_max_recv_data_segment_length);
122         pdu->pdu_data_len = len;
123 }
124
125 static void
126 pdu_send_proxy(struct pdu *pdu)
127 {
128         struct iscsid_connection *conn;
129         struct iscsi_daemon_send ids;
130         int error;
131
132         conn = (struct iscsid_connection *)pdu->pdu_connection;
133         assert(conn->conn_conf.isc_iser != 0);
134
135         pdu_set_data_segment_length(pdu, pdu->pdu_data_len);
136
137         memset(&ids, 0, sizeof(ids));
138         ids.ids_session_id = conn->conn_session_id;
139         ids.ids_bhs = pdu->pdu_bhs;
140         ids.ids_data_segment_len = pdu->pdu_data_len;
141         ids.ids_data_segment = pdu->pdu_data;
142
143         error = ioctl(conn->conn_iscsi_fd, ISCSIDSEND, &ids);
144         if (error != 0)
145                 log_err(1, "ISCSIDSEND");
146 }
147
148 #endif /* ICL_KERNEL_PROXY */
149
150 static void
151 resolve_addr(const struct connection *conn, const char *address,
152     struct addrinfo **ai, bool initiator_side)
153 {
154         struct addrinfo hints;
155         char *arg, *addr, *ch, *tofree;
156         const char *port;
157         int error, colons = 0;
158
159         tofree = arg = checked_strdup(address);
160
161         if (arg[0] == '\0') {
162                 fail(conn, "empty address");
163                 log_errx(1, "empty address");
164         }
165         if (arg[0] == '[') {
166                 /*
167                  * IPv6 address in square brackets, perhaps with port.
168                  */
169                 arg++;
170                 addr = strsep(&arg, "]");
171                 if (arg == NULL) {
172                         fail(conn, "malformed address");
173                         log_errx(1, "malformed address %s", address);
174                 }
175                 if (arg[0] == '\0') {
176                         port = NULL;
177                 } else if (arg[0] == ':') {
178                         port = arg + 1;
179                 } else {
180                         fail(conn, "malformed address");
181                         log_errx(1, "malformed address %s", address);
182                 }
183         } else {
184                 /*
185                  * Either IPv6 address without brackets - and without
186                  * a port - or IPv4 address.  Just count the colons.
187                  */
188                 for (ch = arg; *ch != '\0'; ch++) {
189                         if (*ch == ':')
190                                 colons++;
191                 }
192                 if (colons > 1) {
193                         addr = arg;
194                         port = NULL;
195                 } else {
196                         addr = strsep(&arg, ":");
197                         if (arg == NULL)
198                                 port = NULL;
199                         else
200                                 port = arg;
201                 }
202         }
203
204         if (port == NULL && !initiator_side)
205                 port = "3260";
206
207         memset(&hints, 0, sizeof(hints));
208         hints.ai_family = PF_UNSPEC;
209         hints.ai_socktype = SOCK_STREAM;
210         hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICSERV;
211         if (initiator_side)
212                 hints.ai_flags |= AI_PASSIVE;
213
214         error = getaddrinfo(addr, port, &hints, ai);
215         if (error != 0) {
216                 fail(conn, gai_strerror(error));
217                 log_errx(1, "getaddrinfo for %s failed: %s",
218                     address, gai_strerror(error));
219         }
220
221         free(tofree);
222 }
223
224 static struct iscsid_connection *
225 connection_new(int iscsi_fd, const struct iscsi_daemon_request *request)
226 {
227         struct iscsid_connection *conn;
228         struct addrinfo *from_ai, *to_ai;
229         const char *from_addr, *to_addr;
230 #ifdef ICL_KERNEL_PROXY
231         struct iscsi_daemon_connect idc;
232 #endif
233         int error, optval;
234
235         conn = calloc(1, sizeof(*conn));
236         if (conn == NULL)
237                 log_err(1, "calloc");
238
239         connection_init(&conn->conn, &conn_ops,
240             request->idr_conf.isc_iser != 0);
241         conn->conn_protocol_level = 0;
242         conn->conn_initial_r2t = true;
243         conn->conn_iscsi_fd = iscsi_fd;
244
245         conn->conn_session_id = request->idr_session_id;
246         memcpy(&conn->conn_conf, &request->idr_conf, sizeof(conn->conn_conf));
247         memcpy(&conn->conn.conn_isid, &request->idr_isid,
248             sizeof(conn->conn.conn_isid));
249         conn->conn.conn_tsih = request->idr_tsih;
250
251         from_addr = conn->conn_conf.isc_initiator_addr;
252         to_addr = conn->conn_conf.isc_target_addr;
253
254         if (from_addr[0] != '\0')
255                 resolve_addr(&conn->conn, from_addr, &from_ai, true);
256         else
257                 from_ai = NULL;
258
259         resolve_addr(&conn->conn, to_addr, &to_ai, false);
260
261 #ifdef ICL_KERNEL_PROXY
262         if (conn->conn_conf.isc_iser) {
263                 memset(&idc, 0, sizeof(idc));
264                 idc.idc_session_id = conn->conn_session_id;
265                 if (conn->conn_conf.isc_iser)
266                         idc.idc_iser = 1;
267                 idc.idc_domain = to_ai->ai_family;
268                 idc.idc_socktype = to_ai->ai_socktype;
269                 idc.idc_protocol = to_ai->ai_protocol;
270                 if (from_ai != NULL) {
271                         idc.idc_from_addr = from_ai->ai_addr;
272                         idc.idc_from_addrlen = from_ai->ai_addrlen;
273                 }
274                 idc.idc_to_addr = to_ai->ai_addr;
275                 idc.idc_to_addrlen = to_ai->ai_addrlen;
276
277                 log_debugx("connecting to %s using ICL kernel proxy", to_addr);
278                 error = ioctl(iscsi_fd, ISCSIDCONNECT, &idc);
279                 if (error != 0) {
280                         fail(&conn->conn, strerror(errno));
281                         log_err(1, "failed to connect to %s "
282                             "using ICL kernel proxy: ISCSIDCONNECT", to_addr);
283                 }
284
285                 if (from_ai != NULL)
286                         freeaddrinfo(from_ai);
287                 freeaddrinfo(to_ai);
288
289                 return (conn);
290         }
291 #endif /* ICL_KERNEL_PROXY */
292
293         if (conn->conn_conf.isc_iser) {
294                 fail(&conn->conn, "iSER not supported");
295                 log_errx(1, "iscsid(8) compiled without ICL_KERNEL_PROXY "
296                     "does not support iSER");
297         }
298
299         conn->conn.conn_socket = socket(to_ai->ai_family, to_ai->ai_socktype,
300             to_ai->ai_protocol);
301         if (conn->conn.conn_socket < 0) {
302                 fail(&conn->conn, strerror(errno));
303                 log_err(1, "failed to create socket for %s", from_addr);
304         }
305         optval = SOCKBUF_SIZE;
306         if (setsockopt(conn->conn.conn_socket, SOL_SOCKET, SO_RCVBUF,
307             &optval, sizeof(optval)) == -1)
308                 log_warn("setsockopt(SO_RCVBUF) failed");
309         optval = SOCKBUF_SIZE;
310         if (setsockopt(conn->conn.conn_socket, SOL_SOCKET, SO_SNDBUF,
311             &optval, sizeof(optval)) == -1)
312                 log_warn("setsockopt(SO_SNDBUF) failed");
313         optval = 1;
314         if (setsockopt(conn->conn.conn_socket, SOL_SOCKET, SO_NO_DDP,
315             &optval, sizeof(optval)) == -1)
316                 log_warn("setsockopt(SO_NO_DDP) failed");
317         if (conn->conn_conf.isc_dscp != -1) {
318                 int tos = conn->conn_conf.isc_dscp << 2;
319                 if (to_ai->ai_family == AF_INET) {
320                         if (setsockopt(conn->conn.conn_socket,
321                             IPPROTO_IP, IP_TOS,
322                             &tos, sizeof(tos)) == -1)
323                                 log_warn("setsockopt(IP_TOS) "
324                                     "failed for %s",
325                                     from_addr);
326                 } else
327                 if (to_ai->ai_family == AF_INET6) {
328                         if (setsockopt(conn->conn.conn_socket,
329                             IPPROTO_IPV6, IPV6_TCLASS,
330                             &tos, sizeof(tos)) == -1)
331                                 log_warn("setsockopt(IPV6_TCLASS) "
332                                     "failed for %s",
333                                     from_addr);
334                 }
335         }
336         if (conn->conn_conf.isc_pcp != -1) {
337                 int pcp = conn->conn_conf.isc_pcp;
338                 if (to_ai->ai_family == AF_INET) {
339                         if (setsockopt(conn->conn.conn_socket,
340                             IPPROTO_IP, IP_VLAN_PCP,
341                             &pcp, sizeof(pcp)) == -1)
342                                 log_warn("setsockopt(IP_VLAN_PCP) "
343                                     "failed for %s",
344                                     from_addr);
345                 } else
346                 if (to_ai->ai_family == AF_INET6) {
347                         if (setsockopt(conn->conn.conn_socket,
348                             IPPROTO_IPV6, IPV6_VLAN_PCP,
349                             &pcp, sizeof(pcp)) == -1)
350                                 log_warn("setsockopt(IPV6_VLAN_PCP) "
351                                     "failed for %s",
352                                     from_addr);
353                 }
354         }
355         /*
356          * Reduce TCP SYN_SENT timeout while
357          * no connectivity exists, to allow
358          * rapid reuse of the available slots.
359          */
360         int keepinit = 0;
361         if (conn->conn_conf.isc_login_timeout > 0) {
362                 keepinit = conn->conn_conf.isc_login_timeout;
363                 log_debugx("session specific LoginTimeout at %d sec",
364                         keepinit);
365         }
366         if (conn->conn_conf.isc_login_timeout == -1) {
367                 int value;
368                 size_t size = sizeof(value);
369                 if (sysctlbyname("kern.iscsi.login_timeout",
370                     &value, &size, NULL, 0) == 0) {
371                         keepinit = value;
372                         log_debugx("global login_timeout at %d sec",
373                                 keepinit);
374                 }
375         }
376         if (keepinit > 0) {
377                 if (setsockopt(conn->conn.conn_socket,
378                     IPPROTO_TCP, TCP_KEEPINIT,
379                     &keepinit, sizeof(keepinit)) == -1)
380                         log_warnx("setsockopt(TCP_KEEPINIT) "
381                             "failed for %s", to_addr);
382         }
383         if (from_ai != NULL) {
384                 error = bind(conn->conn.conn_socket, from_ai->ai_addr,
385                     from_ai->ai_addrlen);
386                 if (error != 0) {
387                         fail(&conn->conn, strerror(errno));
388                         log_err(1, "failed to bind to %s", from_addr);
389                 }
390         }
391         log_debugx("connecting to %s", to_addr);
392         error = connect(conn->conn.conn_socket, to_ai->ai_addr,
393             to_ai->ai_addrlen);
394         if (error != 0) {
395                 fail(&conn->conn, strerror(errno));
396                 log_err(1, "failed to connect to %s", to_addr);
397         }
398
399         if (from_ai != NULL)
400                 freeaddrinfo(from_ai);
401         freeaddrinfo(to_ai);
402
403         return (conn);
404 }
405
406 static void
407 limits(struct iscsid_connection *conn)
408 {
409         struct iscsi_daemon_limits idl;
410         struct iscsi_session_limits *isl;
411         int error;
412
413         log_debugx("fetching limits from the kernel");
414
415         memset(&idl, 0, sizeof(idl));
416         idl.idl_session_id = conn->conn_session_id;
417         idl.idl_socket = conn->conn.conn_socket;
418
419         error = ioctl(conn->conn_iscsi_fd, ISCSIDLIMITS, &idl);
420         if (error != 0)
421                 log_err(1, "ISCSIDLIMITS");
422         
423         /*
424          * Read the driver limits and provide reasonable defaults for the ones
425          * the driver doesn't care about.  If a max_snd_dsl is not explicitly
426          * provided by the driver then we'll make sure both conn->max_snd_dsl
427          * and isl->max_snd_dsl are set to the rcv_dsl.  This preserves historic
428          * behavior.
429          */
430         isl = &conn->conn_limits;
431         memcpy(isl, &idl.idl_limits, sizeof(*isl));
432         if (isl->isl_max_recv_data_segment_length == 0)
433                 isl->isl_max_recv_data_segment_length = (1 << 24) - 1;
434         if (isl->isl_max_send_data_segment_length == 0)
435                 isl->isl_max_send_data_segment_length =
436                     isl->isl_max_recv_data_segment_length;
437         if (isl->isl_max_burst_length == 0)
438                 isl->isl_max_burst_length = (1 << 24) - 1;
439         if (isl->isl_first_burst_length == 0)
440                 isl->isl_first_burst_length = (1 << 24) - 1;
441         if (isl->isl_first_burst_length > isl->isl_max_burst_length)
442                 isl->isl_first_burst_length = isl->isl_max_burst_length;
443
444         /*
445          * Limit default send length in case it won't be negotiated.
446          * We can't do it for other limits, since they may affect both
447          * sender and receiver operation, and we must obey defaults.
448          */
449         if (conn->conn.conn_max_send_data_segment_length >
450             isl->isl_max_send_data_segment_length) {
451                 conn->conn.conn_max_send_data_segment_length =
452                     isl->isl_max_send_data_segment_length;
453         }
454 }
455
456 static void
457 handoff(struct iscsid_connection *conn)
458 {
459         struct iscsi_daemon_handoff idh;
460         int error;
461
462         log_debugx("handing off connection to the kernel");
463
464         memset(&idh, 0, sizeof(idh));
465         idh.idh_session_id = conn->conn_session_id;
466         idh.idh_socket = conn->conn.conn_socket;
467         strlcpy(idh.idh_target_alias, conn->conn_target_alias,
468             sizeof(idh.idh_target_alias));
469         idh.idh_tsih = conn->conn.conn_tsih;
470         idh.idh_statsn = conn->conn.conn_statsn;
471         idh.idh_protocol_level = conn->conn_protocol_level;
472         idh.idh_header_digest = conn->conn.conn_header_digest;
473         idh.idh_data_digest = conn->conn.conn_data_digest;
474         idh.idh_initial_r2t = conn->conn_initial_r2t;
475         idh.idh_immediate_data = conn->conn.conn_immediate_data;
476         idh.idh_max_recv_data_segment_length =
477             conn->conn.conn_max_recv_data_segment_length;
478         idh.idh_max_send_data_segment_length =
479             conn->conn.conn_max_send_data_segment_length;
480         idh.idh_max_burst_length = conn->conn.conn_max_burst_length;
481         idh.idh_first_burst_length = conn->conn.conn_first_burst_length;
482
483         error = ioctl(conn->conn_iscsi_fd, ISCSIDHANDOFF, &idh);
484         if (error != 0)
485                 log_err(1, "ISCSIDHANDOFF");
486 }
487
488 void
489 fail(const struct connection *base_conn, const char *reason)
490 {
491         const struct iscsid_connection *conn;
492         struct iscsi_daemon_fail idf;
493         int error, saved_errno;
494
495         conn = (const struct iscsid_connection *)base_conn;
496         saved_errno = errno;
497
498         memset(&idf, 0, sizeof(idf));
499         idf.idf_session_id = conn->conn_session_id;
500         strlcpy(idf.idf_reason, reason, sizeof(idf.idf_reason));
501
502         error = ioctl(conn->conn_iscsi_fd, ISCSIDFAIL, &idf);
503         if (error != 0)
504                 log_err(1, "ISCSIDFAIL");
505
506         errno = saved_errno;
507 }
508
509 /*
510  * XXX: I CANT INTO LATIN
511  */
512 static void
513 capsicate(struct iscsid_connection *conn)
514 {
515         cap_rights_t rights;
516         const unsigned long cmds[] = {
517 #ifdef ICL_KERNEL_PROXY
518                 ISCSIDCONNECT,
519                 ISCSIDSEND,
520                 ISCSIDRECEIVE,
521 #endif
522                 ISCSIDLIMITS,
523                 ISCSIDHANDOFF,
524                 ISCSIDFAIL,
525                 ISCSISADD,
526                 ISCSISREMOVE,
527                 ISCSISMODIFY
528         };
529
530         cap_rights_init(&rights, CAP_IOCTL);
531         if (caph_rights_limit(conn->conn_iscsi_fd, &rights) < 0)
532                 log_err(1, "cap_rights_limit");
533
534         if (caph_ioctls_limit(conn->conn_iscsi_fd, cmds, nitems(cmds)) < 0)
535                 log_err(1, "cap_ioctls_limit");
536
537         if (caph_enter() != 0)
538                 log_err(1, "cap_enter");
539
540         if (cap_sandboxed())
541                 log_debugx("Capsicum capability mode enabled");
542         else
543                 log_warnx("Capsicum capability mode not supported");
544 }
545
546 static bool
547 timed_out(void)
548 {
549
550         return (sigalrm_received);
551 }
552
553 static void
554 sigalrm_handler(int dummy __unused)
555 {
556         /*
557          * It would be easiest to just log an error and exit.  We can't
558          * do this, though, because log_errx() is not signal safe, since
559          * it calls syslog(3).  Instead, set a flag checked by pdu_send()
560          * and pdu_receive(), to call log_errx() there.  Should they fail
561          * to notice, we'll exit here one second later.
562          */
563         if (sigalrm_received) {
564                 /*
565                  * Oh well.  Just give up and quit.
566                  */
567                 _exit(2);
568         }
569
570         sigalrm_received = true;
571 }
572
573 static void
574 set_timeout(int timeout)
575 {
576         struct sigaction sa;
577         struct itimerval itv;
578         int error;
579
580         if (timeout <= 0) {
581                 log_debugx("session timeout disabled");
582                 return;
583         }
584
585         bzero(&sa, sizeof(sa));
586         sa.sa_handler = sigalrm_handler;
587         sigfillset(&sa.sa_mask);
588         error = sigaction(SIGALRM, &sa, NULL);
589         if (error != 0)
590                 log_err(1, "sigaction");
591
592         /*
593          * First SIGALRM will arive after conf_timeout seconds.
594          * If we do nothing, another one will arrive a second later.
595          */
596         bzero(&itv, sizeof(itv));
597         itv.it_interval.tv_sec = 1;
598         itv.it_value.tv_sec = timeout;
599
600         log_debugx("setting session timeout to %d seconds",
601             timeout);
602         error = setitimer(ITIMER_REAL, &itv, NULL);
603         if (error != 0)
604                 log_err(1, "setitimer");
605 }
606
607 static void
608 sigchld_handler(int dummy __unused)
609 {
610
611         /*
612          * The only purpose of this handler is to make SIGCHLD
613          * interrupt the ISCSIDWAIT ioctl(2), so we can call
614          * wait_for_children().
615          */
616 }
617
618 static void
619 register_sigchld(void)
620 {
621         struct sigaction sa;
622         int error;
623
624         bzero(&sa, sizeof(sa));
625         sa.sa_handler = sigchld_handler;
626         sigfillset(&sa.sa_mask);
627         error = sigaction(SIGCHLD, &sa, NULL);
628         if (error != 0)
629                 log_err(1, "sigaction");
630
631 }
632
633 static void
634 handle_request(int iscsi_fd, const struct iscsi_daemon_request *request, int timeout)
635 {
636         struct iscsid_connection *conn;
637
638         log_set_peer_addr(request->idr_conf.isc_target_addr);
639         if (request->idr_conf.isc_target[0] != '\0') {
640                 log_set_peer_name(request->idr_conf.isc_target);
641                 setproctitle("%s (%s)", request->idr_conf.isc_target_addr, request->idr_conf.isc_target);
642         } else {
643                 setproctitle("%s", request->idr_conf.isc_target_addr);
644         }
645
646         conn = connection_new(iscsi_fd, request);
647         capsicate(conn);
648         limits(conn);
649         set_timeout(timeout);
650         login(conn);
651         if (conn->conn_conf.isc_discovery != 0)
652                 discovery(conn);
653         else
654                 handoff(conn);
655
656         log_debugx("nothing more to do; exiting");
657         exit (0);
658 }
659
660 static int
661 wait_for_children(bool block)
662 {
663         pid_t pid;
664         int status;
665         int num = 0;
666
667         for (;;) {
668                 /*
669                  * If "block" is true, wait for at least one process.
670                  */
671                 if (block && num == 0)
672                         pid = wait4(-1, &status, 0, NULL);
673                 else
674                         pid = wait4(-1, &status, WNOHANG, NULL);
675                 if (pid <= 0)
676                         break;
677                 if (WIFSIGNALED(status)) {
678                         log_warnx("child process %d terminated with signal %d",
679                             pid, WTERMSIG(status));
680                 } else if (WEXITSTATUS(status) != 0) {
681                         log_warnx("child process %d terminated with exit status %d",
682                             pid, WEXITSTATUS(status));
683                 } else {
684                         log_debugx("child process %d terminated gracefully", pid);
685                 }
686                 num++;
687         }
688
689         return (num);
690 }
691
692 int
693 main(int argc, char **argv)
694 {
695         int ch, debug = 0, error, iscsi_fd, maxproc = 30, retval, saved_errno,
696             timeout = 60;
697         bool dont_daemonize = false;
698         struct pidfh *pidfh;
699         pid_t pid, otherpid;
700         const char *pidfile_path = DEFAULT_PIDFILE;
701         struct iscsi_daemon_request request;
702
703         while ((ch = getopt(argc, argv, "P:dl:m:t:")) != -1) {
704                 switch (ch) {
705                 case 'P':
706                         pidfile_path = optarg;
707                         break;
708                 case 'd':
709                         dont_daemonize = true;
710                         debug++;
711                         break;
712                 case 'l':
713                         debug = atoi(optarg);
714                         break;
715                 case 'm':
716                         maxproc = atoi(optarg);
717                         break;
718                 case 't':
719                         timeout = atoi(optarg);
720                         break;
721                 case '?':
722                 default:
723                         usage();
724                 }
725         }
726         argc -= optind;
727         if (argc != 0)
728                 usage();
729
730         log_init(debug);
731
732         pidfh = pidfile_open(pidfile_path, 0600, &otherpid);
733         if (pidfh == NULL) {
734                 if (errno == EEXIST)
735                         log_errx(1, "daemon already running, pid: %jd.",
736                             (intmax_t)otherpid);
737                 log_err(1, "cannot open or create pidfile \"%s\"",
738                     pidfile_path);
739         }
740
741         iscsi_fd = open(ISCSI_PATH, O_RDWR);
742         if (iscsi_fd < 0 && errno == ENOENT) {
743                 saved_errno = errno;
744                 retval = kldload("iscsi");
745                 if (retval != -1)
746                         iscsi_fd = open(ISCSI_PATH, O_RDWR);
747                 else
748                         errno = saved_errno;
749         }
750         if (iscsi_fd < 0)
751                 log_err(1, "failed to open %s", ISCSI_PATH);
752
753         if (dont_daemonize == false) {
754                 if (daemon(0, 0) == -1) {
755                         log_warn("cannot daemonize");
756                         pidfile_remove(pidfh);
757                         exit(1);
758                 }
759         }
760
761         pidfile_write(pidfh);
762
763         register_sigchld();
764
765         for (;;) {
766                 log_debugx("waiting for request from the kernel");
767
768                 memset(&request, 0, sizeof(request));
769                 error = ioctl(iscsi_fd, ISCSIDWAIT, &request);
770                 if (error != 0) {
771                         if (errno == EINTR) {
772                                 nchildren -= wait_for_children(false);
773                                 assert(nchildren >= 0);
774                                 continue;
775                         }
776
777                         log_err(1, "ISCSIDWAIT");
778                 }
779
780                 if (dont_daemonize) {
781                         log_debugx("not forking due to -d flag; "
782                             "will exit after servicing a single request");
783                 } else {
784                         nchildren -= wait_for_children(false);
785                         assert(nchildren >= 0);
786
787                         while (maxproc > 0 && nchildren >= maxproc) {
788                                 log_debugx("maxproc limit of %d child processes hit; "
789                                     "waiting for child process to exit", maxproc);
790                                 nchildren -= wait_for_children(true);
791                                 assert(nchildren >= 0);
792                         }
793                         log_debugx("incoming connection; forking child process #%d",
794                             nchildren);
795                         nchildren++;
796
797                         pid = fork();
798                         if (pid < 0)
799                                 log_err(1, "fork");
800                         if (pid > 0)
801                                 continue;
802                 }
803
804                 pidfile_close(pidfh);
805                 handle_request(iscsi_fd, &request, timeout);
806         }
807
808         return (0);
809 }