1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 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 iscsi_session_limits *isl; 229 struct addrinfo *from_ai, *to_ai; 230 const char *from_addr, *to_addr; 231 #ifdef ICL_KERNEL_PROXY 232 struct iscsi_daemon_connect idc; 233 #endif 234 int error, optval; 235 236 conn = calloc(1, sizeof(*conn)); 237 if (conn == NULL) 238 log_err(1, "calloc"); 239 240 connection_init(&conn->conn, &conn_ops, 241 request->idr_conf.isc_iser != 0); 242 conn->conn_protocol_level = 0; 243 conn->conn_initial_r2t = true; 244 conn->conn_iscsi_fd = iscsi_fd; 245 246 conn->conn_session_id = request->idr_session_id; 247 memcpy(&conn->conn_conf, &request->idr_conf, sizeof(conn->conn_conf)); 248 memcpy(&conn->conn.conn_isid, &request->idr_isid, 249 sizeof(conn->conn.conn_isid)); 250 conn->conn.conn_tsih = request->idr_tsih; 251 252 /* 253 * Read the driver limits and provide reasonable defaults for the ones 254 * the driver doesn't care about. If a max_snd_dsl is not explicitly 255 * provided by the driver then we'll make sure both conn->max_snd_dsl 256 * and isl->max_snd_dsl are set to the rcv_dsl. This preserves historic 257 * behavior. 258 */ 259 isl = &conn->conn_limits; 260 memcpy(isl, &request->idr_limits, sizeof(*isl)); 261 if (isl->isl_max_recv_data_segment_length == 0) 262 isl->isl_max_recv_data_segment_length = (1 << 24) - 1; 263 if (isl->isl_max_send_data_segment_length == 0) 264 isl->isl_max_send_data_segment_length = 265 isl->isl_max_recv_data_segment_length; 266 if (isl->isl_max_burst_length == 0) 267 isl->isl_max_burst_length = (1 << 24) - 1; 268 if (isl->isl_first_burst_length == 0) 269 isl->isl_first_burst_length = (1 << 24) - 1; 270 if (isl->isl_first_burst_length > isl->isl_max_burst_length) 271 isl->isl_first_burst_length = isl->isl_max_burst_length; 272 273 /* 274 * Limit default send length in case it won't be negotiated. 275 * We can't do it for other limits, since they may affect both 276 * sender and receiver operation, and we must obey defaults. 277 */ 278 if (conn->conn.conn_max_send_data_segment_length > 279 isl->isl_max_send_data_segment_length) { 280 conn->conn.conn_max_send_data_segment_length = 281 isl->isl_max_send_data_segment_length; 282 } 283 284 from_addr = conn->conn_conf.isc_initiator_addr; 285 to_addr = conn->conn_conf.isc_target_addr; 286 287 if (from_addr[0] != '\0') 288 resolve_addr(&conn->conn, from_addr, &from_ai, true); 289 else 290 from_ai = NULL; 291 292 resolve_addr(&conn->conn, to_addr, &to_ai, false); 293 294 #ifdef ICL_KERNEL_PROXY 295 if (conn->conn_conf.isc_iser) { 296 memset(&idc, 0, sizeof(idc)); 297 idc.idc_session_id = conn->conn_session_id; 298 if (conn->conn_conf.isc_iser) 299 idc.idc_iser = 1; 300 idc.idc_domain = to_ai->ai_family; 301 idc.idc_socktype = to_ai->ai_socktype; 302 idc.idc_protocol = to_ai->ai_protocol; 303 if (from_ai != NULL) { 304 idc.idc_from_addr = from_ai->ai_addr; 305 idc.idc_from_addrlen = from_ai->ai_addrlen; 306 } 307 idc.idc_to_addr = to_ai->ai_addr; 308 idc.idc_to_addrlen = to_ai->ai_addrlen; 309 310 log_debugx("connecting to %s using ICL kernel proxy", to_addr); 311 error = ioctl(iscsi_fd, ISCSIDCONNECT, &idc); 312 if (error != 0) { 313 fail(&conn->conn, strerror(errno)); 314 log_err(1, "failed to connect to %s " 315 "using ICL kernel proxy: ISCSIDCONNECT", to_addr); 316 } 317 318 if (from_ai != NULL) 319 freeaddrinfo(from_ai); 320 freeaddrinfo(to_ai); 321 322 return (conn); 323 } 324 #endif /* ICL_KERNEL_PROXY */ 325 326 if (conn->conn_conf.isc_iser) { 327 fail(&conn->conn, "iSER not supported"); 328 log_errx(1, "iscsid(8) compiled without ICL_KERNEL_PROXY " 329 "does not support iSER"); 330 } 331 332 conn->conn.conn_socket = socket(to_ai->ai_family, to_ai->ai_socktype, 333 to_ai->ai_protocol); 334 if (conn->conn.conn_socket < 0) { 335 fail(&conn->conn, strerror(errno)); 336 log_err(1, "failed to create socket for %s", from_addr); 337 } 338 optval = SOCKBUF_SIZE; 339 if (setsockopt(conn->conn.conn_socket, SOL_SOCKET, SO_RCVBUF, 340 &optval, sizeof(optval)) == -1) 341 log_warn("setsockopt(SO_RCVBUF) failed"); 342 optval = SOCKBUF_SIZE; 343 if (setsockopt(conn->conn.conn_socket, SOL_SOCKET, SO_SNDBUF, 344 &optval, sizeof(optval)) == -1) 345 log_warn("setsockopt(SO_SNDBUF) failed"); 346 optval = 1; 347 if (setsockopt(conn->conn.conn_socket, SOL_SOCKET, SO_NO_DDP, 348 &optval, sizeof(optval)) == -1) 349 log_warn("setsockopt(SO_NO_DDP) failed"); 350 if (conn->conn_conf.isc_dscp != -1) { 351 int tos = conn->conn_conf.isc_dscp << 2; 352 if (to_ai->ai_family == AF_INET) { 353 if (setsockopt(conn->conn.conn_socket, 354 IPPROTO_IP, IP_TOS, 355 &tos, sizeof(tos)) == -1) 356 log_warn("setsockopt(IP_TOS) " 357 "failed for %s", 358 from_addr); 359 } else 360 if (to_ai->ai_family == AF_INET6) { 361 if (setsockopt(conn->conn.conn_socket, 362 IPPROTO_IPV6, IPV6_TCLASS, 363 &tos, sizeof(tos)) == -1) 364 log_warn("setsockopt(IPV6_TCLASS) " 365 "failed for %s", 366 from_addr); 367 } 368 } 369 if (conn->conn_conf.isc_pcp != -1) { 370 int pcp = conn->conn_conf.isc_pcp; 371 if (to_ai->ai_family == AF_INET) { 372 if (setsockopt(conn->conn.conn_socket, 373 IPPROTO_IP, IP_VLAN_PCP, 374 &pcp, sizeof(pcp)) == -1) 375 log_warn("setsockopt(IP_VLAN_PCP) " 376 "failed for %s", 377 from_addr); 378 } else 379 if (to_ai->ai_family == AF_INET6) { 380 if (setsockopt(conn->conn.conn_socket, 381 IPPROTO_IPV6, IPV6_VLAN_PCP, 382 &pcp, sizeof(pcp)) == -1) 383 log_warn("setsockopt(IPV6_VLAN_PCP) " 384 "failed for %s", 385 from_addr); 386 } 387 } 388 /* 389 * Reduce TCP SYN_SENT timeout while 390 * no connectivity exists, to allow 391 * rapid reuse of the available slots. 392 */ 393 int keepinit = 0; 394 if (conn->conn_conf.isc_login_timeout > 0) { 395 keepinit = conn->conn_conf.isc_login_timeout; 396 log_debugx("session specific LoginTimeout at %d sec", 397 keepinit); 398 } 399 if (conn->conn_conf.isc_login_timeout == -1) { 400 int value; 401 size_t size = sizeof(value); 402 if (sysctlbyname("kern.iscsi.login_timeout", 403 &value, &size, NULL, 0) == 0) { 404 keepinit = value; 405 log_debugx("global login_timeout at %d sec", 406 keepinit); 407 } 408 } 409 if (keepinit > 0) { 410 if (setsockopt(conn->conn.conn_socket, 411 IPPROTO_TCP, TCP_KEEPINIT, 412 &keepinit, sizeof(keepinit)) == -1) 413 log_warnx("setsockopt(TCP_KEEPINIT) " 414 "failed for %s", to_addr); 415 } 416 if (from_ai != NULL) { 417 error = bind(conn->conn.conn_socket, from_ai->ai_addr, 418 from_ai->ai_addrlen); 419 if (error != 0) { 420 fail(&conn->conn, strerror(errno)); 421 log_err(1, "failed to bind to %s", from_addr); 422 } 423 } 424 log_debugx("connecting to %s", to_addr); 425 error = connect(conn->conn.conn_socket, to_ai->ai_addr, 426 to_ai->ai_addrlen); 427 if (error != 0) { 428 fail(&conn->conn, strerror(errno)); 429 log_err(1, "failed to connect to %s", to_addr); 430 } 431 432 if (from_ai != NULL) 433 freeaddrinfo(from_ai); 434 freeaddrinfo(to_ai); 435 436 return (conn); 437 } 438 439 static void 440 handoff(struct iscsid_connection *conn) 441 { 442 struct iscsi_daemon_handoff idh; 443 int error; 444 445 log_debugx("handing off connection to the kernel"); 446 447 memset(&idh, 0, sizeof(idh)); 448 idh.idh_session_id = conn->conn_session_id; 449 idh.idh_socket = conn->conn.conn_socket; 450 strlcpy(idh.idh_target_alias, conn->conn_target_alias, 451 sizeof(idh.idh_target_alias)); 452 idh.idh_tsih = conn->conn.conn_tsih; 453 idh.idh_statsn = conn->conn.conn_statsn; 454 idh.idh_protocol_level = conn->conn_protocol_level; 455 idh.idh_header_digest = conn->conn.conn_header_digest; 456 idh.idh_data_digest = conn->conn.conn_data_digest; 457 idh.idh_initial_r2t = conn->conn_initial_r2t; 458 idh.idh_immediate_data = conn->conn.conn_immediate_data; 459 idh.idh_max_recv_data_segment_length = 460 conn->conn.conn_max_recv_data_segment_length; 461 idh.idh_max_send_data_segment_length = 462 conn->conn.conn_max_send_data_segment_length; 463 idh.idh_max_burst_length = conn->conn.conn_max_burst_length; 464 idh.idh_first_burst_length = conn->conn.conn_first_burst_length; 465 466 error = ioctl(conn->conn_iscsi_fd, ISCSIDHANDOFF, &idh); 467 if (error != 0) 468 log_err(1, "ISCSIDHANDOFF"); 469 } 470 471 void 472 fail(const struct connection *base_conn, const char *reason) 473 { 474 const struct iscsid_connection *conn; 475 struct iscsi_daemon_fail idf; 476 int error, saved_errno; 477 478 conn = (const struct iscsid_connection *)base_conn; 479 saved_errno = errno; 480 481 memset(&idf, 0, sizeof(idf)); 482 idf.idf_session_id = conn->conn_session_id; 483 strlcpy(idf.idf_reason, reason, sizeof(idf.idf_reason)); 484 485 error = ioctl(conn->conn_iscsi_fd, ISCSIDFAIL, &idf); 486 if (error != 0) 487 log_err(1, "ISCSIDFAIL"); 488 489 errno = saved_errno; 490 } 491 492 /* 493 * XXX: I CANT INTO LATIN 494 */ 495 static void 496 capsicate(struct iscsid_connection *conn) 497 { 498 cap_rights_t rights; 499 #ifdef ICL_KERNEL_PROXY 500 const unsigned long cmds[] = { ISCSIDCONNECT, ISCSIDSEND, ISCSIDRECEIVE, 501 ISCSIDHANDOFF, ISCSIDFAIL, ISCSISADD, ISCSISREMOVE, ISCSISMODIFY }; 502 #else 503 const unsigned long cmds[] = { ISCSIDHANDOFF, ISCSIDFAIL, ISCSISADD, 504 ISCSISREMOVE, ISCSISMODIFY }; 505 #endif 506 507 cap_rights_init(&rights, CAP_IOCTL); 508 if (caph_rights_limit(conn->conn_iscsi_fd, &rights) < 0) 509 log_err(1, "cap_rights_limit"); 510 511 if (caph_ioctls_limit(conn->conn_iscsi_fd, cmds, nitems(cmds)) < 0) 512 log_err(1, "cap_ioctls_limit"); 513 514 if (caph_enter() != 0) 515 log_err(1, "cap_enter"); 516 517 if (cap_sandboxed()) 518 log_debugx("Capsicum capability mode enabled"); 519 else 520 log_warnx("Capsicum capability mode not supported"); 521 } 522 523 static bool 524 timed_out(void) 525 { 526 527 return (sigalrm_received); 528 } 529 530 static void 531 sigalrm_handler(int dummy __unused) 532 { 533 /* 534 * It would be easiest to just log an error and exit. We can't 535 * do this, though, because log_errx() is not signal safe, since 536 * it calls syslog(3). Instead, set a flag checked by pdu_send() 537 * and pdu_receive(), to call log_errx() there. Should they fail 538 * to notice, we'll exit here one second later. 539 */ 540 if (sigalrm_received) { 541 /* 542 * Oh well. Just give up and quit. 543 */ 544 _exit(2); 545 } 546 547 sigalrm_received = true; 548 } 549 550 static void 551 set_timeout(int timeout) 552 { 553 struct sigaction sa; 554 struct itimerval itv; 555 int error; 556 557 if (timeout <= 0) { 558 log_debugx("session timeout disabled"); 559 return; 560 } 561 562 bzero(&sa, sizeof(sa)); 563 sa.sa_handler = sigalrm_handler; 564 sigfillset(&sa.sa_mask); 565 error = sigaction(SIGALRM, &sa, NULL); 566 if (error != 0) 567 log_err(1, "sigaction"); 568 569 /* 570 * First SIGALRM will arive after conf_timeout seconds. 571 * If we do nothing, another one will arrive a second later. 572 */ 573 bzero(&itv, sizeof(itv)); 574 itv.it_interval.tv_sec = 1; 575 itv.it_value.tv_sec = timeout; 576 577 log_debugx("setting session timeout to %d seconds", 578 timeout); 579 error = setitimer(ITIMER_REAL, &itv, NULL); 580 if (error != 0) 581 log_err(1, "setitimer"); 582 } 583 584 static void 585 sigchld_handler(int dummy __unused) 586 { 587 588 /* 589 * The only purpose of this handler is to make SIGCHLD 590 * interrupt the ISCSIDWAIT ioctl(2), so we can call 591 * wait_for_children(). 592 */ 593 } 594 595 static void 596 register_sigchld(void) 597 { 598 struct sigaction sa; 599 int error; 600 601 bzero(&sa, sizeof(sa)); 602 sa.sa_handler = sigchld_handler; 603 sigfillset(&sa.sa_mask); 604 error = sigaction(SIGCHLD, &sa, NULL); 605 if (error != 0) 606 log_err(1, "sigaction"); 607 608 } 609 610 static void 611 handle_request(int iscsi_fd, const struct iscsi_daemon_request *request, int timeout) 612 { 613 struct iscsid_connection *conn; 614 615 log_set_peer_addr(request->idr_conf.isc_target_addr); 616 if (request->idr_conf.isc_target[0] != '\0') { 617 log_set_peer_name(request->idr_conf.isc_target); 618 setproctitle("%s (%s)", request->idr_conf.isc_target_addr, request->idr_conf.isc_target); 619 } else { 620 setproctitle("%s", request->idr_conf.isc_target_addr); 621 } 622 623 conn = connection_new(iscsi_fd, request); 624 set_timeout(timeout); 625 capsicate(conn); 626 login(conn); 627 if (conn->conn_conf.isc_discovery != 0) 628 discovery(conn); 629 else 630 handoff(conn); 631 632 log_debugx("nothing more to do; exiting"); 633 exit (0); 634 } 635 636 static int 637 wait_for_children(bool block) 638 { 639 pid_t pid; 640 int status; 641 int num = 0; 642 643 for (;;) { 644 /* 645 * If "block" is true, wait for at least one process. 646 */ 647 if (block && num == 0) 648 pid = wait4(-1, &status, 0, NULL); 649 else 650 pid = wait4(-1, &status, WNOHANG, NULL); 651 if (pid <= 0) 652 break; 653 if (WIFSIGNALED(status)) { 654 log_warnx("child process %d terminated with signal %d", 655 pid, WTERMSIG(status)); 656 } else if (WEXITSTATUS(status) != 0) { 657 log_warnx("child process %d terminated with exit status %d", 658 pid, WEXITSTATUS(status)); 659 } else { 660 log_debugx("child process %d terminated gracefully", pid); 661 } 662 num++; 663 } 664 665 return (num); 666 } 667 668 int 669 main(int argc, char **argv) 670 { 671 int ch, debug = 0, error, iscsi_fd, maxproc = 30, retval, saved_errno, 672 timeout = 60; 673 bool dont_daemonize = false; 674 struct pidfh *pidfh; 675 pid_t pid, otherpid; 676 const char *pidfile_path = DEFAULT_PIDFILE; 677 struct iscsi_daemon_request request; 678 679 while ((ch = getopt(argc, argv, "P:dl:m:t:")) != -1) { 680 switch (ch) { 681 case 'P': 682 pidfile_path = optarg; 683 break; 684 case 'd': 685 dont_daemonize = true; 686 debug++; 687 break; 688 case 'l': 689 debug = atoi(optarg); 690 break; 691 case 'm': 692 maxproc = atoi(optarg); 693 break; 694 case 't': 695 timeout = atoi(optarg); 696 break; 697 case '?': 698 default: 699 usage(); 700 } 701 } 702 argc -= optind; 703 if (argc != 0) 704 usage(); 705 706 log_init(debug); 707 708 pidfh = pidfile_open(pidfile_path, 0600, &otherpid); 709 if (pidfh == NULL) { 710 if (errno == EEXIST) 711 log_errx(1, "daemon already running, pid: %jd.", 712 (intmax_t)otherpid); 713 log_err(1, "cannot open or create pidfile \"%s\"", 714 pidfile_path); 715 } 716 717 iscsi_fd = open(ISCSI_PATH, O_RDWR); 718 if (iscsi_fd < 0 && errno == ENOENT) { 719 saved_errno = errno; 720 retval = kldload("iscsi"); 721 if (retval != -1) 722 iscsi_fd = open(ISCSI_PATH, O_RDWR); 723 else 724 errno = saved_errno; 725 } 726 if (iscsi_fd < 0) 727 log_err(1, "failed to open %s", ISCSI_PATH); 728 729 if (dont_daemonize == false) { 730 if (daemon(0, 0) == -1) { 731 log_warn("cannot daemonize"); 732 pidfile_remove(pidfh); 733 exit(1); 734 } 735 } 736 737 pidfile_write(pidfh); 738 739 register_sigchld(); 740 741 for (;;) { 742 log_debugx("waiting for request from the kernel"); 743 744 memset(&request, 0, sizeof(request)); 745 error = ioctl(iscsi_fd, ISCSIDWAIT, &request); 746 if (error != 0) { 747 if (errno == EINTR) { 748 nchildren -= wait_for_children(false); 749 assert(nchildren >= 0); 750 continue; 751 } 752 753 log_err(1, "ISCSIDWAIT"); 754 } 755 756 if (dont_daemonize) { 757 log_debugx("not forking due to -d flag; " 758 "will exit after servicing a single request"); 759 } else { 760 nchildren -= wait_for_children(false); 761 assert(nchildren >= 0); 762 763 while (maxproc > 0 && nchildren >= maxproc) { 764 log_debugx("maxproc limit of %d child processes hit; " 765 "waiting for child process to exit", maxproc); 766 nchildren -= wait_for_children(true); 767 assert(nchildren >= 0); 768 } 769 log_debugx("incoming connection; forking child process #%d", 770 nchildren); 771 nchildren++; 772 773 pid = fork(); 774 if (pid < 0) 775 log_err(1, "fork"); 776 if (pid > 0) 777 continue; 778 } 779 780 pidfile_close(pidfh); 781 handle_request(iscsi_fd, &request, timeout); 782 } 783 784 return (0); 785 } 786