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