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