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 } 810