ctld.c revision 265509
1255570Strasz/*- 2255570Strasz * Copyright (c) 2012 The FreeBSD Foundation 3255570Strasz * All rights reserved. 4255570Strasz * 5255570Strasz * This software was developed by Edward Tomasz Napierala under sponsorship 6255570Strasz * from the FreeBSD Foundation. 7255570Strasz * 8255570Strasz * Redistribution and use in source and binary forms, with or without 9255570Strasz * modification, are permitted provided that the following conditions 10255570Strasz * are met: 11255570Strasz * 1. Redistributions of source code must retain the above copyright 12255570Strasz * notice, this list of conditions and the following disclaimer. 13255570Strasz * 2. Redistributions in binary form must reproduce the above copyright 14255570Strasz * notice, this list of conditions and the following disclaimer in the 15255570Strasz * documentation and/or other materials provided with the distribution. 16255570Strasz * 17255570Strasz * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18255570Strasz * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19255570Strasz * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20255570Strasz * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21255570Strasz * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22255570Strasz * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23255570Strasz * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24255570Strasz * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25255570Strasz * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26255570Strasz * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27255570Strasz * SUCH DAMAGE. 28255570Strasz * 29255570Strasz * $FreeBSD: stable/10/usr.sbin/ctld/ctld.c 265509 2014-05-07 07:32:45Z trasz $ 30255570Strasz */ 31255570Strasz 32255570Strasz#include <sys/types.h> 33255570Strasz#include <sys/time.h> 34255570Strasz#include <sys/socket.h> 35255570Strasz#include <sys/wait.h> 36255570Strasz#include <netinet/in.h> 37255570Strasz#include <assert.h> 38255570Strasz#include <ctype.h> 39255570Strasz#include <errno.h> 40255570Strasz#include <netdb.h> 41255570Strasz#include <signal.h> 42255570Strasz#include <stdbool.h> 43255570Strasz#include <stdio.h> 44255570Strasz#include <stdint.h> 45255570Strasz#include <stdlib.h> 46255570Strasz#include <string.h> 47255570Strasz#include <unistd.h> 48255570Strasz 49255570Strasz#include "ctld.h" 50255570Strasz 51265507Straszbool proxy_mode = false; 52265507Strasz 53255570Straszstatic volatile bool sighup_received = false; 54255570Straszstatic volatile bool sigterm_received = false; 55255570Straszstatic volatile bool sigalrm_received = false; 56255570Strasz 57255570Straszstatic int nchildren = 0; 58255570Strasz 59255570Straszstatic void 60255570Straszusage(void) 61255570Strasz{ 62255570Strasz 63255570Strasz fprintf(stderr, "usage: ctld [-d][-f config-file]\n"); 64255570Strasz exit(1); 65255570Strasz} 66255570Strasz 67255570Straszchar * 68255570Straszchecked_strdup(const char *s) 69255570Strasz{ 70255570Strasz char *c; 71255570Strasz 72255570Strasz c = strdup(s); 73255570Strasz if (c == NULL) 74255570Strasz log_err(1, "strdup"); 75255570Strasz return (c); 76255570Strasz} 77255570Strasz 78255570Straszstruct conf * 79255570Straszconf_new(void) 80255570Strasz{ 81255570Strasz struct conf *conf; 82255570Strasz 83255570Strasz conf = calloc(1, sizeof(*conf)); 84255570Strasz if (conf == NULL) 85255570Strasz log_err(1, "calloc"); 86255570Strasz TAILQ_INIT(&conf->conf_targets); 87255570Strasz TAILQ_INIT(&conf->conf_auth_groups); 88255570Strasz TAILQ_INIT(&conf->conf_portal_groups); 89255570Strasz 90255570Strasz conf->conf_debug = 0; 91255570Strasz conf->conf_timeout = 60; 92255570Strasz conf->conf_maxproc = 30; 93255570Strasz 94255570Strasz return (conf); 95255570Strasz} 96255570Strasz 97255570Straszvoid 98255570Straszconf_delete(struct conf *conf) 99255570Strasz{ 100255570Strasz struct target *targ, *tmp; 101255570Strasz struct auth_group *ag, *cagtmp; 102255570Strasz struct portal_group *pg, *cpgtmp; 103255570Strasz 104255570Strasz assert(conf->conf_pidfh == NULL); 105255570Strasz 106255570Strasz TAILQ_FOREACH_SAFE(targ, &conf->conf_targets, t_next, tmp) 107255570Strasz target_delete(targ); 108255570Strasz TAILQ_FOREACH_SAFE(ag, &conf->conf_auth_groups, ag_next, cagtmp) 109255570Strasz auth_group_delete(ag); 110255570Strasz TAILQ_FOREACH_SAFE(pg, &conf->conf_portal_groups, pg_next, cpgtmp) 111255570Strasz portal_group_delete(pg); 112255570Strasz free(conf->conf_pidfile_path); 113255570Strasz free(conf); 114255570Strasz} 115255570Strasz 116255570Straszstatic struct auth * 117255570Straszauth_new(struct auth_group *ag) 118255570Strasz{ 119255570Strasz struct auth *auth; 120255570Strasz 121255570Strasz auth = calloc(1, sizeof(*auth)); 122255570Strasz if (auth == NULL) 123255570Strasz log_err(1, "calloc"); 124255570Strasz auth->a_auth_group = ag; 125255570Strasz TAILQ_INSERT_TAIL(&ag->ag_auths, auth, a_next); 126255570Strasz return (auth); 127255570Strasz} 128255570Strasz 129255570Straszstatic void 130255570Straszauth_delete(struct auth *auth) 131255570Strasz{ 132255570Strasz TAILQ_REMOVE(&auth->a_auth_group->ag_auths, auth, a_next); 133255570Strasz 134255570Strasz free(auth->a_user); 135255570Strasz free(auth->a_secret); 136255570Strasz free(auth->a_mutual_user); 137255570Strasz free(auth->a_mutual_secret); 138255570Strasz free(auth); 139255570Strasz} 140255570Strasz 141255570Straszconst struct auth * 142255570Straszauth_find(struct auth_group *ag, const char *user) 143255570Strasz{ 144255570Strasz const struct auth *auth; 145255570Strasz 146255570Strasz TAILQ_FOREACH(auth, &ag->ag_auths, a_next) { 147255570Strasz if (strcmp(auth->a_user, user) == 0) 148255570Strasz return (auth); 149255570Strasz } 150255570Strasz 151255570Strasz return (NULL); 152255570Strasz} 153255570Strasz 154263721Straszstatic void 155263721Straszauth_check_secret_length(struct auth *auth) 156263721Strasz{ 157263721Strasz size_t len; 158263721Strasz 159263721Strasz len = strlen(auth->a_secret); 160263721Strasz if (len > 16) { 161263721Strasz if (auth->a_auth_group->ag_name != NULL) 162263721Strasz log_warnx("secret for user \"%s\", auth-group \"%s\", " 163263721Strasz "is too long; it should be at most 16 characters " 164263721Strasz "long", auth->a_user, auth->a_auth_group->ag_name); 165263721Strasz else 166263721Strasz log_warnx("secret for user \"%s\", target \"%s\", " 167263721Strasz "is too long; it should be at most 16 characters " 168263721Strasz "long", auth->a_user, 169263723Strasz auth->a_auth_group->ag_target->t_name); 170263721Strasz } 171263721Strasz if (len < 12) { 172263721Strasz if (auth->a_auth_group->ag_name != NULL) 173263721Strasz log_warnx("secret for user \"%s\", auth-group \"%s\", " 174263721Strasz "is too short; it should be at least 12 characters " 175263721Strasz "long", auth->a_user, 176263721Strasz auth->a_auth_group->ag_name); 177263721Strasz else 178263721Strasz log_warnx("secret for user \"%s\", target \"%s\", " 179263721Strasz "is too short; it should be at least 16 characters " 180263721Strasz "long", auth->a_user, 181263723Strasz auth->a_auth_group->ag_target->t_name); 182263721Strasz } 183263721Strasz 184263721Strasz if (auth->a_mutual_secret != NULL) { 185263721Strasz len = strlen(auth->a_secret); 186263721Strasz if (len > 16) { 187263721Strasz if (auth->a_auth_group->ag_name != NULL) 188263721Strasz log_warnx("mutual secret for user \"%s\", " 189263721Strasz "auth-group \"%s\", is too long; it should " 190263721Strasz "be at most 16 characters long", 191263721Strasz auth->a_user, auth->a_auth_group->ag_name); 192263721Strasz else 193263721Strasz log_warnx("mutual secret for user \"%s\", " 194263721Strasz "target \"%s\", is too long; it should " 195263721Strasz "be at most 16 characters long", 196263721Strasz auth->a_user, 197263723Strasz auth->a_auth_group->ag_target->t_name); 198263721Strasz } 199263721Strasz if (len < 12) { 200263721Strasz if (auth->a_auth_group->ag_name != NULL) 201263721Strasz log_warnx("mutual secret for user \"%s\", " 202263721Strasz "auth-group \"%s\", is too short; it " 203263721Strasz "should be at least 12 characters long", 204263721Strasz auth->a_user, auth->a_auth_group->ag_name); 205263721Strasz else 206263721Strasz log_warnx("mutual secret for user \"%s\", " 207263721Strasz "target \"%s\", is too short; it should be " 208263721Strasz "at least 16 characters long", 209263721Strasz auth->a_user, 210263723Strasz auth->a_auth_group->ag_target->t_name); 211263721Strasz } 212263721Strasz } 213263721Strasz} 214263721Strasz 215263721Straszconst struct auth * 216263721Straszauth_new_chap(struct auth_group *ag, const char *user, 217263721Strasz const char *secret) 218263721Strasz{ 219263721Strasz struct auth *auth; 220263721Strasz 221263721Strasz if (ag->ag_type == AG_TYPE_UNKNOWN) 222263721Strasz ag->ag_type = AG_TYPE_CHAP; 223263721Strasz if (ag->ag_type != AG_TYPE_CHAP) { 224263721Strasz if (ag->ag_name != NULL) 225263721Strasz log_warnx("cannot mix \"chap\" authentication with " 226263721Strasz "other types for auth-group \"%s\"", ag->ag_name); 227263721Strasz else 228263721Strasz log_warnx("cannot mix \"chap\" authentication with " 229263721Strasz "other types for target \"%s\"", 230263723Strasz ag->ag_target->t_name); 231263721Strasz return (NULL); 232263721Strasz } 233263721Strasz 234263721Strasz auth = auth_new(ag); 235263721Strasz auth->a_user = checked_strdup(user); 236263721Strasz auth->a_secret = checked_strdup(secret); 237263721Strasz 238263721Strasz auth_check_secret_length(auth); 239263721Strasz 240263721Strasz return (auth); 241263721Strasz} 242263721Strasz 243263721Straszconst struct auth * 244263721Straszauth_new_chap_mutual(struct auth_group *ag, const char *user, 245263721Strasz const char *secret, const char *user2, const char *secret2) 246263721Strasz{ 247263721Strasz struct auth *auth; 248263721Strasz 249263721Strasz if (ag->ag_type == AG_TYPE_UNKNOWN) 250263721Strasz ag->ag_type = AG_TYPE_CHAP_MUTUAL; 251263721Strasz if (ag->ag_type != AG_TYPE_CHAP_MUTUAL) { 252263721Strasz if (ag->ag_name != NULL) 253263721Strasz log_warnx("cannot mix \"chap-mutual\" authentication " 254263721Strasz "with other types for auth-group \"%s\"", 255263721Strasz ag->ag_name); 256263721Strasz else 257263721Strasz log_warnx("cannot mix \"chap-mutual\" authentication " 258263721Strasz "with other types for target \"%s\"", 259263723Strasz ag->ag_target->t_name); 260263721Strasz return (NULL); 261263721Strasz } 262263721Strasz 263263721Strasz auth = auth_new(ag); 264263721Strasz auth->a_user = checked_strdup(user); 265263721Strasz auth->a_secret = checked_strdup(secret); 266263721Strasz auth->a_mutual_user = checked_strdup(user2); 267263721Strasz auth->a_mutual_secret = checked_strdup(secret2); 268263721Strasz 269263721Strasz auth_check_secret_length(auth); 270263721Strasz 271263721Strasz return (auth); 272263721Strasz} 273263721Strasz 274263720Straszconst struct auth_name * 275263720Straszauth_name_new(struct auth_group *ag, const char *name) 276263720Strasz{ 277263720Strasz struct auth_name *an; 278263720Strasz 279263720Strasz an = calloc(1, sizeof(*an)); 280263720Strasz if (an == NULL) 281263720Strasz log_err(1, "calloc"); 282263720Strasz an->an_auth_group = ag; 283263720Strasz an->an_initator_name = checked_strdup(name); 284263720Strasz TAILQ_INSERT_TAIL(&ag->ag_names, an, an_next); 285263720Strasz return (an); 286263720Strasz} 287263720Strasz 288263720Straszstatic void 289263720Straszauth_name_delete(struct auth_name *an) 290263720Strasz{ 291263720Strasz TAILQ_REMOVE(&an->an_auth_group->ag_names, an, an_next); 292263720Strasz 293263720Strasz free(an->an_initator_name); 294263720Strasz free(an); 295263720Strasz} 296263720Strasz 297263720Straszbool 298263720Straszauth_name_defined(const struct auth_group *ag) 299263720Strasz{ 300263720Strasz if (TAILQ_EMPTY(&ag->ag_names)) 301263720Strasz return (false); 302263720Strasz return (true); 303263720Strasz} 304263720Strasz 305263720Straszconst struct auth_name * 306263720Straszauth_name_find(const struct auth_group *ag, const char *name) 307263720Strasz{ 308263720Strasz const struct auth_name *auth_name; 309263720Strasz 310263720Strasz TAILQ_FOREACH(auth_name, &ag->ag_names, an_next) { 311263720Strasz if (strcmp(auth_name->an_initator_name, name) == 0) 312263720Strasz return (auth_name); 313263720Strasz } 314263720Strasz 315263720Strasz return (NULL); 316263720Strasz} 317263720Strasz 318263720Straszconst struct auth_portal * 319263720Straszauth_portal_new(struct auth_group *ag, const char *portal) 320263720Strasz{ 321263720Strasz struct auth_portal *ap; 322263720Strasz 323263720Strasz ap = calloc(1, sizeof(*ap)); 324263720Strasz if (ap == NULL) 325263720Strasz log_err(1, "calloc"); 326263720Strasz ap->ap_auth_group = ag; 327263720Strasz ap->ap_initator_portal = checked_strdup(portal); 328263720Strasz TAILQ_INSERT_TAIL(&ag->ag_portals, ap, ap_next); 329263720Strasz return (ap); 330263720Strasz} 331263720Strasz 332263720Straszstatic void 333263720Straszauth_portal_delete(struct auth_portal *ap) 334263720Strasz{ 335263720Strasz TAILQ_REMOVE(&ap->ap_auth_group->ag_portals, ap, ap_next); 336263720Strasz 337263720Strasz free(ap->ap_initator_portal); 338263720Strasz free(ap); 339263720Strasz} 340263720Strasz 341263720Straszbool 342263720Straszauth_portal_defined(const struct auth_group *ag) 343263720Strasz{ 344263720Strasz if (TAILQ_EMPTY(&ag->ag_portals)) 345263720Strasz return (false); 346263720Strasz return (true); 347263720Strasz} 348263720Strasz 349263720Straszconst struct auth_portal * 350263720Straszauth_portal_find(const struct auth_group *ag, const char *portal) 351263720Strasz{ 352263720Strasz const struct auth_portal *auth_portal; 353263720Strasz 354263720Strasz TAILQ_FOREACH(auth_portal, &ag->ag_portals, ap_next) { 355263720Strasz if (strcmp(auth_portal->ap_initator_portal, portal) == 0) 356263720Strasz return (auth_portal); 357263720Strasz } 358263720Strasz 359263720Strasz return (NULL); 360263720Strasz} 361263720Strasz 362255570Straszstruct auth_group * 363255570Straszauth_group_new(struct conf *conf, const char *name) 364255570Strasz{ 365255570Strasz struct auth_group *ag; 366255570Strasz 367255570Strasz if (name != NULL) { 368255570Strasz ag = auth_group_find(conf, name); 369255570Strasz if (ag != NULL) { 370255570Strasz log_warnx("duplicated auth-group \"%s\"", name); 371255570Strasz return (NULL); 372255570Strasz } 373255570Strasz } 374255570Strasz 375255570Strasz ag = calloc(1, sizeof(*ag)); 376255570Strasz if (ag == NULL) 377255570Strasz log_err(1, "calloc"); 378255570Strasz if (name != NULL) 379255570Strasz ag->ag_name = checked_strdup(name); 380255570Strasz TAILQ_INIT(&ag->ag_auths); 381263720Strasz TAILQ_INIT(&ag->ag_names); 382263720Strasz TAILQ_INIT(&ag->ag_portals); 383255570Strasz ag->ag_conf = conf; 384255570Strasz TAILQ_INSERT_TAIL(&conf->conf_auth_groups, ag, ag_next); 385255570Strasz 386255570Strasz return (ag); 387255570Strasz} 388255570Strasz 389255570Straszvoid 390255570Straszauth_group_delete(struct auth_group *ag) 391255570Strasz{ 392263720Strasz struct auth *auth, *auth_tmp; 393263720Strasz struct auth_name *auth_name, *auth_name_tmp; 394263720Strasz struct auth_portal *auth_portal, *auth_portal_tmp; 395255570Strasz 396255570Strasz TAILQ_REMOVE(&ag->ag_conf->conf_auth_groups, ag, ag_next); 397255570Strasz 398263720Strasz TAILQ_FOREACH_SAFE(auth, &ag->ag_auths, a_next, auth_tmp) 399255570Strasz auth_delete(auth); 400263720Strasz TAILQ_FOREACH_SAFE(auth_name, &ag->ag_names, an_next, auth_name_tmp) 401263720Strasz auth_name_delete(auth_name); 402263720Strasz TAILQ_FOREACH_SAFE(auth_portal, &ag->ag_portals, ap_next, 403263720Strasz auth_portal_tmp) 404263720Strasz auth_portal_delete(auth_portal); 405255570Strasz free(ag->ag_name); 406255570Strasz free(ag); 407255570Strasz} 408255570Strasz 409255570Straszstruct auth_group * 410255570Straszauth_group_find(struct conf *conf, const char *name) 411255570Strasz{ 412255570Strasz struct auth_group *ag; 413255570Strasz 414255570Strasz TAILQ_FOREACH(ag, &conf->conf_auth_groups, ag_next) { 415255570Strasz if (ag->ag_name != NULL && strcmp(ag->ag_name, name) == 0) 416255570Strasz return (ag); 417255570Strasz } 418255570Strasz 419255570Strasz return (NULL); 420255570Strasz} 421255570Strasz 422263724Straszstatic int 423263724Straszauth_group_set_type(struct auth_group *ag, int type) 424263724Strasz{ 425263724Strasz 426263724Strasz if (ag->ag_type == AG_TYPE_UNKNOWN) { 427263724Strasz ag->ag_type = type; 428263724Strasz return (0); 429263724Strasz } 430263724Strasz 431263724Strasz if (ag->ag_type == type) 432263724Strasz return (0); 433263724Strasz 434263724Strasz return (1); 435263724Strasz} 436263724Strasz 437263724Straszint 438263724Straszauth_group_set_type_str(struct auth_group *ag, const char *str) 439263724Strasz{ 440263724Strasz int error, type; 441263724Strasz 442263724Strasz if (strcmp(str, "none") == 0) { 443263724Strasz type = AG_TYPE_NO_AUTHENTICATION; 444263729Strasz } else if (strcmp(str, "deny") == 0) { 445263729Strasz type = AG_TYPE_DENY; 446263724Strasz } else if (strcmp(str, "chap") == 0) { 447263724Strasz type = AG_TYPE_CHAP; 448263724Strasz } else if (strcmp(str, "chap-mutual") == 0) { 449263724Strasz type = AG_TYPE_CHAP_MUTUAL; 450263724Strasz } else { 451263724Strasz if (ag->ag_name != NULL) 452263724Strasz log_warnx("invalid auth-type \"%s\" for auth-group " 453263724Strasz "\"%s\"", str, ag->ag_name); 454263724Strasz else 455263724Strasz log_warnx("invalid auth-type \"%s\" for target " 456263724Strasz "\"%s\"", str, ag->ag_target->t_name); 457263724Strasz return (1); 458263724Strasz } 459263724Strasz 460263724Strasz error = auth_group_set_type(ag, type); 461263724Strasz if (error != 0) { 462263724Strasz if (ag->ag_name != NULL) 463263724Strasz log_warnx("cannot set auth-type to \"%s\" for " 464263724Strasz "auth-group \"%s\"; already has a different " 465263724Strasz "type", str, ag->ag_name); 466263724Strasz else 467263724Strasz log_warnx("cannot set auth-type to \"%s\" for target " 468263724Strasz "\"%s\"; already has a different type", 469263724Strasz str, ag->ag_target->t_name); 470263724Strasz return (1); 471263724Strasz } 472263724Strasz 473263724Strasz return (error); 474263724Strasz} 475263724Strasz 476255570Straszstatic struct portal * 477255570Straszportal_new(struct portal_group *pg) 478255570Strasz{ 479255570Strasz struct portal *portal; 480255570Strasz 481255570Strasz portal = calloc(1, sizeof(*portal)); 482255570Strasz if (portal == NULL) 483255570Strasz log_err(1, "calloc"); 484255570Strasz TAILQ_INIT(&portal->p_targets); 485255570Strasz portal->p_portal_group = pg; 486255570Strasz TAILQ_INSERT_TAIL(&pg->pg_portals, portal, p_next); 487255570Strasz return (portal); 488255570Strasz} 489255570Strasz 490255570Straszstatic void 491255570Straszportal_delete(struct portal *portal) 492255570Strasz{ 493255570Strasz TAILQ_REMOVE(&portal->p_portal_group->pg_portals, portal, p_next); 494255570Strasz freeaddrinfo(portal->p_ai); 495255570Strasz free(portal->p_listen); 496255570Strasz free(portal); 497255570Strasz} 498255570Strasz 499255570Straszstruct portal_group * 500255570Straszportal_group_new(struct conf *conf, const char *name) 501255570Strasz{ 502255570Strasz struct portal_group *pg; 503255570Strasz 504255678Strasz pg = portal_group_find(conf, name); 505255678Strasz if (pg != NULL) { 506255678Strasz log_warnx("duplicated portal-group \"%s\"", name); 507255678Strasz return (NULL); 508255570Strasz } 509255570Strasz 510255570Strasz pg = calloc(1, sizeof(*pg)); 511255570Strasz if (pg == NULL) 512255570Strasz log_err(1, "calloc"); 513255570Strasz pg->pg_name = checked_strdup(name); 514255570Strasz TAILQ_INIT(&pg->pg_portals); 515255570Strasz pg->pg_conf = conf; 516255570Strasz conf->conf_last_portal_group_tag++; 517255570Strasz pg->pg_tag = conf->conf_last_portal_group_tag; 518255570Strasz TAILQ_INSERT_TAIL(&conf->conf_portal_groups, pg, pg_next); 519255570Strasz 520255570Strasz return (pg); 521255570Strasz} 522255570Strasz 523255570Straszvoid 524255570Straszportal_group_delete(struct portal_group *pg) 525255570Strasz{ 526255570Strasz struct portal *portal, *tmp; 527255570Strasz 528255570Strasz TAILQ_REMOVE(&pg->pg_conf->conf_portal_groups, pg, pg_next); 529255570Strasz 530255570Strasz TAILQ_FOREACH_SAFE(portal, &pg->pg_portals, p_next, tmp) 531255570Strasz portal_delete(portal); 532255570Strasz free(pg->pg_name); 533255570Strasz free(pg); 534255570Strasz} 535255570Strasz 536255570Straszstruct portal_group * 537255570Straszportal_group_find(struct conf *conf, const char *name) 538255570Strasz{ 539255570Strasz struct portal_group *pg; 540255570Strasz 541255570Strasz TAILQ_FOREACH(pg, &conf->conf_portal_groups, pg_next) { 542255570Strasz if (strcmp(pg->pg_name, name) == 0) 543255570Strasz return (pg); 544255570Strasz } 545255570Strasz 546255570Strasz return (NULL); 547255570Strasz} 548255570Strasz 549255570Straszint 550255570Straszportal_group_add_listen(struct portal_group *pg, const char *value, bool iser) 551255570Strasz{ 552255570Strasz struct addrinfo hints; 553255570Strasz struct portal *portal; 554255570Strasz char *addr, *ch, *arg; 555255570Strasz const char *port; 556255570Strasz int error, colons = 0; 557255570Strasz 558255570Strasz portal = portal_new(pg); 559255570Strasz portal->p_listen = checked_strdup(value); 560255570Strasz portal->p_iser = iser; 561255570Strasz 562255570Strasz arg = portal->p_listen; 563255570Strasz if (arg[0] == '\0') { 564255570Strasz log_warnx("empty listen address"); 565255570Strasz free(portal->p_listen); 566255570Strasz free(portal); 567255570Strasz return (1); 568255570Strasz } 569255570Strasz if (arg[0] == '[') { 570255570Strasz /* 571255570Strasz * IPv6 address in square brackets, perhaps with port. 572255570Strasz */ 573255570Strasz arg++; 574255570Strasz addr = strsep(&arg, "]"); 575255570Strasz if (arg == NULL) { 576255570Strasz log_warnx("invalid listen address %s", 577255570Strasz portal->p_listen); 578255570Strasz free(portal->p_listen); 579255570Strasz free(portal); 580255570Strasz return (1); 581255570Strasz } 582255570Strasz if (arg[0] == '\0') { 583255570Strasz port = "3260"; 584255570Strasz } else if (arg[0] == ':') { 585255570Strasz port = arg + 1; 586255570Strasz } else { 587255570Strasz log_warnx("invalid listen address %s", 588255570Strasz portal->p_listen); 589255570Strasz free(portal->p_listen); 590255570Strasz free(portal); 591255570Strasz return (1); 592255570Strasz } 593255570Strasz } else { 594255570Strasz /* 595255570Strasz * Either IPv6 address without brackets - and without 596255570Strasz * a port - or IPv4 address. Just count the colons. 597255570Strasz */ 598255570Strasz for (ch = arg; *ch != '\0'; ch++) { 599255570Strasz if (*ch == ':') 600255570Strasz colons++; 601255570Strasz } 602255570Strasz if (colons > 1) { 603255570Strasz addr = arg; 604255570Strasz port = "3260"; 605255570Strasz } else { 606255570Strasz addr = strsep(&arg, ":"); 607255570Strasz if (arg == NULL) 608255570Strasz port = "3260"; 609255570Strasz else 610255570Strasz port = arg; 611255570Strasz } 612255570Strasz } 613255570Strasz 614255570Strasz memset(&hints, 0, sizeof(hints)); 615255570Strasz hints.ai_family = PF_UNSPEC; 616255570Strasz hints.ai_socktype = SOCK_STREAM; 617255570Strasz hints.ai_flags = AI_PASSIVE; 618255570Strasz 619255570Strasz error = getaddrinfo(addr, port, &hints, &portal->p_ai); 620255570Strasz if (error != 0) { 621255570Strasz log_warnx("getaddrinfo for %s failed: %s", 622255570Strasz portal->p_listen, gai_strerror(error)); 623255570Strasz free(portal->p_listen); 624255570Strasz free(portal); 625255570Strasz return (1); 626255570Strasz } 627255570Strasz 628255570Strasz /* 629255570Strasz * XXX: getaddrinfo(3) may return multiple addresses; we should turn 630255570Strasz * those into multiple portals. 631255570Strasz */ 632255570Strasz 633255570Strasz return (0); 634255570Strasz} 635255570Strasz 636255570Straszstatic bool 637255570Straszvalid_hex(const char ch) 638255570Strasz{ 639255570Strasz switch (ch) { 640255570Strasz case '0': 641255570Strasz case '1': 642255570Strasz case '2': 643255570Strasz case '3': 644255570Strasz case '4': 645255570Strasz case '5': 646255570Strasz case '6': 647255570Strasz case '7': 648255570Strasz case '8': 649255570Strasz case '9': 650255570Strasz case 'a': 651255570Strasz case 'A': 652255570Strasz case 'b': 653255570Strasz case 'B': 654255570Strasz case 'c': 655255570Strasz case 'C': 656255570Strasz case 'd': 657255570Strasz case 'D': 658255570Strasz case 'e': 659255570Strasz case 'E': 660255570Strasz case 'f': 661255570Strasz case 'F': 662255570Strasz return (true); 663255570Strasz default: 664255570Strasz return (false); 665255570Strasz } 666255570Strasz} 667255570Strasz 668255570Straszbool 669255570Straszvalid_iscsi_name(const char *name) 670255570Strasz{ 671255570Strasz int i; 672255570Strasz 673255570Strasz if (strlen(name) >= MAX_NAME_LEN) { 674255570Strasz log_warnx("overlong name for target \"%s\"; max length allowed " 675255570Strasz "by iSCSI specification is %d characters", 676255570Strasz name, MAX_NAME_LEN); 677255570Strasz return (false); 678255570Strasz } 679255570Strasz 680255570Strasz /* 681255570Strasz * In the cases below, we don't return an error, just in case the admin 682255570Strasz * was right, and we're wrong. 683255570Strasz */ 684255570Strasz if (strncasecmp(name, "iqn.", strlen("iqn.")) == 0) { 685255570Strasz for (i = strlen("iqn."); name[i] != '\0'; i++) { 686255570Strasz /* 687255570Strasz * XXX: We should verify UTF-8 normalisation, as defined 688255570Strasz * by 3.2.6.2: iSCSI Name Encoding. 689255570Strasz */ 690255570Strasz if (isalnum(name[i])) 691255570Strasz continue; 692255570Strasz if (name[i] == '-' || name[i] == '.' || name[i] == ':') 693255570Strasz continue; 694255570Strasz log_warnx("invalid character \"%c\" in target name " 695255570Strasz "\"%s\"; allowed characters are letters, digits, " 696255570Strasz "'-', '.', and ':'", name[i], name); 697255570Strasz break; 698255570Strasz } 699255570Strasz /* 700255570Strasz * XXX: Check more stuff: valid date and a valid reversed domain. 701255570Strasz */ 702255570Strasz } else if (strncasecmp(name, "eui.", strlen("eui.")) == 0) { 703255570Strasz if (strlen(name) != strlen("eui.") + 16) 704255570Strasz log_warnx("invalid target name \"%s\"; the \"eui.\" " 705255570Strasz "should be followed by exactly 16 hexadecimal " 706255570Strasz "digits", name); 707255570Strasz for (i = strlen("eui."); name[i] != '\0'; i++) { 708255570Strasz if (!valid_hex(name[i])) { 709255570Strasz log_warnx("invalid character \"%c\" in target " 710255570Strasz "name \"%s\"; allowed characters are 1-9 " 711255570Strasz "and A-F", name[i], name); 712255570Strasz break; 713255570Strasz } 714255570Strasz } 715255570Strasz } else if (strncasecmp(name, "naa.", strlen("naa.")) == 0) { 716255570Strasz if (strlen(name) > strlen("naa.") + 32) 717255570Strasz log_warnx("invalid target name \"%s\"; the \"naa.\" " 718255570Strasz "should be followed by at most 32 hexadecimal " 719255570Strasz "digits", name); 720255570Strasz for (i = strlen("naa."); name[i] != '\0'; i++) { 721255570Strasz if (!valid_hex(name[i])) { 722255570Strasz log_warnx("invalid character \"%c\" in target " 723255570Strasz "name \"%s\"; allowed characters are 1-9 " 724255570Strasz "and A-F", name[i], name); 725255570Strasz break; 726255570Strasz } 727255570Strasz } 728255570Strasz } else { 729255570Strasz log_warnx("invalid target name \"%s\"; should start with " 730255570Strasz "either \".iqn\", \"eui.\", or \"naa.\"", 731255570Strasz name); 732255570Strasz } 733255570Strasz return (true); 734255570Strasz} 735255570Strasz 736255570Straszstruct target * 737263723Strasztarget_new(struct conf *conf, const char *name) 738255570Strasz{ 739255570Strasz struct target *targ; 740255570Strasz int i, len; 741255570Strasz 742263723Strasz targ = target_find(conf, name); 743255570Strasz if (targ != NULL) { 744263723Strasz log_warnx("duplicated target \"%s\"", name); 745255570Strasz return (NULL); 746255570Strasz } 747263723Strasz if (valid_iscsi_name(name) == false) { 748263723Strasz log_warnx("target name \"%s\" is invalid", name); 749255570Strasz return (NULL); 750255570Strasz } 751255570Strasz targ = calloc(1, sizeof(*targ)); 752255570Strasz if (targ == NULL) 753255570Strasz log_err(1, "calloc"); 754263723Strasz targ->t_name = checked_strdup(name); 755255570Strasz 756255570Strasz /* 757255570Strasz * RFC 3722 requires us to normalize the name to lowercase. 758255570Strasz */ 759263723Strasz len = strlen(name); 760255570Strasz for (i = 0; i < len; i++) 761263723Strasz targ->t_name[i] = tolower(targ->t_name[i]); 762255570Strasz 763255570Strasz TAILQ_INIT(&targ->t_luns); 764255570Strasz targ->t_conf = conf; 765255570Strasz TAILQ_INSERT_TAIL(&conf->conf_targets, targ, t_next); 766255570Strasz 767255570Strasz return (targ); 768255570Strasz} 769255570Strasz 770255570Straszvoid 771255570Strasztarget_delete(struct target *targ) 772255570Strasz{ 773255570Strasz struct lun *lun, *tmp; 774255570Strasz 775255570Strasz TAILQ_REMOVE(&targ->t_conf->conf_targets, targ, t_next); 776255570Strasz 777255570Strasz TAILQ_FOREACH_SAFE(lun, &targ->t_luns, l_next, tmp) 778255570Strasz lun_delete(lun); 779263723Strasz free(targ->t_name); 780255570Strasz free(targ); 781255570Strasz} 782255570Strasz 783255570Straszstruct target * 784263723Strasztarget_find(struct conf *conf, const char *name) 785255570Strasz{ 786255570Strasz struct target *targ; 787255570Strasz 788255570Strasz TAILQ_FOREACH(targ, &conf->conf_targets, t_next) { 789263723Strasz if (strcasecmp(targ->t_name, name) == 0) 790255570Strasz return (targ); 791255570Strasz } 792255570Strasz 793255570Strasz return (NULL); 794255570Strasz} 795255570Strasz 796255570Straszstruct lun * 797255570Straszlun_new(struct target *targ, int lun_id) 798255570Strasz{ 799255570Strasz struct lun *lun; 800255570Strasz 801255570Strasz lun = lun_find(targ, lun_id); 802255570Strasz if (lun != NULL) { 803255570Strasz log_warnx("duplicated lun %d for target \"%s\"", 804263723Strasz lun_id, targ->t_name); 805255570Strasz return (NULL); 806255570Strasz } 807255570Strasz 808255570Strasz lun = calloc(1, sizeof(*lun)); 809255570Strasz if (lun == NULL) 810255570Strasz log_err(1, "calloc"); 811255570Strasz lun->l_lun = lun_id; 812255570Strasz TAILQ_INIT(&lun->l_options); 813255570Strasz lun->l_target = targ; 814255570Strasz TAILQ_INSERT_TAIL(&targ->t_luns, lun, l_next); 815255570Strasz 816255570Strasz return (lun); 817255570Strasz} 818255570Strasz 819255570Straszvoid 820255570Straszlun_delete(struct lun *lun) 821255570Strasz{ 822255570Strasz struct lun_option *lo, *tmp; 823255570Strasz 824255570Strasz TAILQ_REMOVE(&lun->l_target->t_luns, lun, l_next); 825255570Strasz 826255570Strasz TAILQ_FOREACH_SAFE(lo, &lun->l_options, lo_next, tmp) 827255570Strasz lun_option_delete(lo); 828255570Strasz free(lun->l_backend); 829255570Strasz free(lun->l_device_id); 830255570Strasz free(lun->l_path); 831255570Strasz free(lun->l_serial); 832255570Strasz free(lun); 833255570Strasz} 834255570Strasz 835255570Straszstruct lun * 836255570Straszlun_find(struct target *targ, int lun_id) 837255570Strasz{ 838255570Strasz struct lun *lun; 839255570Strasz 840255570Strasz TAILQ_FOREACH(lun, &targ->t_luns, l_next) { 841255570Strasz if (lun->l_lun == lun_id) 842255570Strasz return (lun); 843255570Strasz } 844255570Strasz 845255570Strasz return (NULL); 846255570Strasz} 847255570Strasz 848255570Straszvoid 849255570Straszlun_set_backend(struct lun *lun, const char *value) 850255570Strasz{ 851255570Strasz free(lun->l_backend); 852255570Strasz lun->l_backend = checked_strdup(value); 853255570Strasz} 854255570Strasz 855255570Straszvoid 856255570Straszlun_set_blocksize(struct lun *lun, size_t value) 857255570Strasz{ 858255570Strasz 859255570Strasz lun->l_blocksize = value; 860255570Strasz} 861255570Strasz 862255570Straszvoid 863255570Straszlun_set_device_id(struct lun *lun, const char *value) 864255570Strasz{ 865255570Strasz free(lun->l_device_id); 866255570Strasz lun->l_device_id = checked_strdup(value); 867255570Strasz} 868255570Strasz 869255570Straszvoid 870255570Straszlun_set_path(struct lun *lun, const char *value) 871255570Strasz{ 872255570Strasz free(lun->l_path); 873255570Strasz lun->l_path = checked_strdup(value); 874255570Strasz} 875255570Strasz 876255570Straszvoid 877255570Straszlun_set_serial(struct lun *lun, const char *value) 878255570Strasz{ 879255570Strasz free(lun->l_serial); 880255570Strasz lun->l_serial = checked_strdup(value); 881255570Strasz} 882255570Strasz 883255570Straszvoid 884255570Straszlun_set_size(struct lun *lun, size_t value) 885255570Strasz{ 886255570Strasz 887255570Strasz lun->l_size = value; 888255570Strasz} 889255570Strasz 890255570Straszvoid 891255570Straszlun_set_ctl_lun(struct lun *lun, uint32_t value) 892255570Strasz{ 893255570Strasz 894255570Strasz lun->l_ctl_lun = value; 895255570Strasz} 896255570Strasz 897255570Straszstruct lun_option * 898255570Straszlun_option_new(struct lun *lun, const char *name, const char *value) 899255570Strasz{ 900255570Strasz struct lun_option *lo; 901255570Strasz 902255570Strasz lo = lun_option_find(lun, name); 903255570Strasz if (lo != NULL) { 904255570Strasz log_warnx("duplicated lun option %s for lun %d, target \"%s\"", 905263723Strasz name, lun->l_lun, lun->l_target->t_name); 906255570Strasz return (NULL); 907255570Strasz } 908255570Strasz 909255570Strasz lo = calloc(1, sizeof(*lo)); 910255570Strasz if (lo == NULL) 911255570Strasz log_err(1, "calloc"); 912255570Strasz lo->lo_name = checked_strdup(name); 913255570Strasz lo->lo_value = checked_strdup(value); 914255570Strasz lo->lo_lun = lun; 915255570Strasz TAILQ_INSERT_TAIL(&lun->l_options, lo, lo_next); 916255570Strasz 917255570Strasz return (lo); 918255570Strasz} 919255570Strasz 920255570Straszvoid 921255570Straszlun_option_delete(struct lun_option *lo) 922255570Strasz{ 923255570Strasz 924255570Strasz TAILQ_REMOVE(&lo->lo_lun->l_options, lo, lo_next); 925255570Strasz 926255570Strasz free(lo->lo_name); 927255570Strasz free(lo->lo_value); 928255570Strasz free(lo); 929255570Strasz} 930255570Strasz 931255570Straszstruct lun_option * 932255570Straszlun_option_find(struct lun *lun, const char *name) 933255570Strasz{ 934255570Strasz struct lun_option *lo; 935255570Strasz 936255570Strasz TAILQ_FOREACH(lo, &lun->l_options, lo_next) { 937255570Strasz if (strcmp(lo->lo_name, name) == 0) 938255570Strasz return (lo); 939255570Strasz } 940255570Strasz 941255570Strasz return (NULL); 942255570Strasz} 943255570Strasz 944255570Straszvoid 945255570Straszlun_option_set(struct lun_option *lo, const char *value) 946255570Strasz{ 947255570Strasz 948255570Strasz free(lo->lo_value); 949255570Strasz lo->lo_value = checked_strdup(value); 950255570Strasz} 951255570Strasz 952255570Straszstatic struct connection * 953255570Straszconnection_new(struct portal *portal, int fd, const char *host) 954255570Strasz{ 955255570Strasz struct connection *conn; 956255570Strasz 957255570Strasz conn = calloc(1, sizeof(*conn)); 958255570Strasz if (conn == NULL) 959255570Strasz log_err(1, "calloc"); 960255570Strasz conn->conn_portal = portal; 961255570Strasz conn->conn_socket = fd; 962255570Strasz conn->conn_initiator_addr = checked_strdup(host); 963255570Strasz 964255570Strasz /* 965255570Strasz * Default values, from RFC 3720, section 12. 966255570Strasz */ 967255570Strasz conn->conn_max_data_segment_length = 8192; 968255570Strasz conn->conn_max_burst_length = 262144; 969255570Strasz conn->conn_immediate_data = true; 970255570Strasz 971255570Strasz return (conn); 972255570Strasz} 973255570Strasz 974255570Strasz#if 0 975255570Straszstatic void 976255570Straszconf_print(struct conf *conf) 977255570Strasz{ 978255570Strasz struct auth_group *ag; 979255570Strasz struct auth *auth; 980263720Strasz struct auth_name *auth_name; 981263720Strasz struct auth_portal *auth_portal; 982255570Strasz struct portal_group *pg; 983255570Strasz struct portal *portal; 984255570Strasz struct target *targ; 985255570Strasz struct lun *lun; 986255570Strasz struct lun_option *lo; 987255570Strasz 988255570Strasz TAILQ_FOREACH(ag, &conf->conf_auth_groups, ag_next) { 989255570Strasz fprintf(stderr, "auth-group %s {\n", ag->ag_name); 990255570Strasz TAILQ_FOREACH(auth, &ag->ag_auths, a_next) 991255570Strasz fprintf(stderr, "\t chap-mutual %s %s %s %s\n", 992255570Strasz auth->a_user, auth->a_secret, 993255570Strasz auth->a_mutual_user, auth->a_mutual_secret); 994263720Strasz TAILQ_FOREACH(auth_name, &ag->ag_names, an_next) 995263720Strasz fprintf(stderr, "\t initiator-name %s\n", 996263720Strasz auth_name->an_initator_name); 997263720Strasz TAILQ_FOREACH(auth_portal, &ag->ag_portals, an_next) 998263720Strasz fprintf(stderr, "\t initiator-portal %s\n", 999263720Strasz auth_portal->an_initator_portal); 1000255570Strasz fprintf(stderr, "}\n"); 1001255570Strasz } 1002255570Strasz TAILQ_FOREACH(pg, &conf->conf_portal_groups, pg_next) { 1003255570Strasz fprintf(stderr, "portal-group %s {\n", pg->pg_name); 1004255570Strasz TAILQ_FOREACH(portal, &pg->pg_portals, p_next) 1005255570Strasz fprintf(stderr, "\t listen %s\n", portal->p_listen); 1006255570Strasz fprintf(stderr, "}\n"); 1007255570Strasz } 1008255570Strasz TAILQ_FOREACH(targ, &conf->conf_targets, t_next) { 1009263723Strasz fprintf(stderr, "target %s {\n", targ->t_name); 1010255570Strasz if (targ->t_alias != NULL) 1011255570Strasz fprintf(stderr, "\t alias %s\n", targ->t_alias); 1012255570Strasz TAILQ_FOREACH(lun, &targ->t_luns, l_next) { 1013255570Strasz fprintf(stderr, "\tlun %d {\n", lun->l_lun); 1014255570Strasz fprintf(stderr, "\t\tpath %s\n", lun->l_path); 1015255570Strasz TAILQ_FOREACH(lo, &lun->l_options, lo_next) 1016255570Strasz fprintf(stderr, "\t\toption %s %s\n", 1017255570Strasz lo->lo_name, lo->lo_value); 1018255570Strasz fprintf(stderr, "\t}\n"); 1019255570Strasz } 1020255570Strasz fprintf(stderr, "}\n"); 1021255570Strasz } 1022255570Strasz} 1023255570Strasz#endif 1024255570Strasz 1025263717Straszstatic int 1026263717Straszconf_verify_lun(struct lun *lun) 1027263717Strasz{ 1028263717Strasz const struct lun *lun2; 1029263718Strasz const struct target *targ2; 1030263717Strasz 1031263717Strasz if (lun->l_backend == NULL) 1032263717Strasz lun_set_backend(lun, "block"); 1033263717Strasz if (strcmp(lun->l_backend, "block") == 0) { 1034263717Strasz if (lun->l_path == NULL) { 1035263717Strasz log_warnx("missing path for lun %d, target \"%s\"", 1036263723Strasz lun->l_lun, lun->l_target->t_name); 1037263717Strasz return (1); 1038263717Strasz } 1039263717Strasz } else if (strcmp(lun->l_backend, "ramdisk") == 0) { 1040263717Strasz if (lun->l_size == 0) { 1041263717Strasz log_warnx("missing size for ramdisk-backed lun %d, " 1042263723Strasz "target \"%s\"", lun->l_lun, lun->l_target->t_name); 1043263717Strasz return (1); 1044263717Strasz } 1045263717Strasz if (lun->l_path != NULL) { 1046263717Strasz log_warnx("path must not be specified " 1047263717Strasz "for ramdisk-backed lun %d, target \"%s\"", 1048263723Strasz lun->l_lun, lun->l_target->t_name); 1049263717Strasz return (1); 1050263717Strasz } 1051263717Strasz } 1052263717Strasz if (lun->l_lun < 0 || lun->l_lun > 255) { 1053263717Strasz log_warnx("invalid lun number for lun %d, target \"%s\"; " 1054263717Strasz "must be between 0 and 255", lun->l_lun, 1055263723Strasz lun->l_target->t_name); 1056263717Strasz return (1); 1057263717Strasz } 1058263717Strasz if (lun->l_blocksize == 0) { 1059263717Strasz lun_set_blocksize(lun, DEFAULT_BLOCKSIZE); 1060263717Strasz } else if (lun->l_blocksize < 0) { 1061263717Strasz log_warnx("invalid blocksize for lun %d, target \"%s\"; " 1062263723Strasz "must be larger than 0", lun->l_lun, lun->l_target->t_name); 1063263717Strasz return (1); 1064263717Strasz } 1065263717Strasz if (lun->l_size != 0 && lun->l_size % lun->l_blocksize != 0) { 1066263717Strasz log_warnx("invalid size for lun %d, target \"%s\"; " 1067263717Strasz "must be multiple of blocksize", lun->l_lun, 1068263723Strasz lun->l_target->t_name); 1069263717Strasz return (1); 1070263717Strasz } 1071263718Strasz TAILQ_FOREACH(targ2, &lun->l_target->t_conf->conf_targets, t_next) { 1072263718Strasz TAILQ_FOREACH(lun2, &targ2->t_luns, l_next) { 1073263718Strasz if (lun == lun2) 1074263718Strasz continue; 1075263718Strasz if (lun->l_path != NULL && lun2->l_path != NULL && 1076263718Strasz strcmp(lun->l_path, lun2->l_path) == 0) { 1077263718Strasz log_debugx("WARNING: path \"%s\" duplicated " 1078263718Strasz "between lun %d, target \"%s\", and " 1079263718Strasz "lun %d, target \"%s\"", lun->l_path, 1080263723Strasz lun->l_lun, lun->l_target->t_name, 1081263723Strasz lun2->l_lun, lun2->l_target->t_name); 1082263718Strasz } 1083263718Strasz } 1084263718Strasz } 1085263717Strasz 1086263717Strasz return (0); 1087263717Strasz} 1088263717Strasz 1089255570Straszint 1090255570Straszconf_verify(struct conf *conf) 1091255570Strasz{ 1092255570Strasz struct auth_group *ag; 1093255570Strasz struct portal_group *pg; 1094255570Strasz struct target *targ; 1095263717Strasz struct lun *lun; 1096265506Strasz bool found_lun; 1097263717Strasz int error; 1098255570Strasz 1099255570Strasz if (conf->conf_pidfile_path == NULL) 1100255570Strasz conf->conf_pidfile_path = checked_strdup(DEFAULT_PIDFILE); 1101255570Strasz 1102255570Strasz TAILQ_FOREACH(targ, &conf->conf_targets, t_next) { 1103255570Strasz if (targ->t_auth_group == NULL) { 1104263726Strasz targ->t_auth_group = auth_group_find(conf, 1105263726Strasz "default"); 1106263726Strasz assert(targ->t_auth_group != NULL); 1107255570Strasz } 1108255570Strasz if (targ->t_portal_group == NULL) { 1109255570Strasz targ->t_portal_group = portal_group_find(conf, 1110255570Strasz "default"); 1111255570Strasz assert(targ->t_portal_group != NULL); 1112255570Strasz } 1113265506Strasz found_lun = false; 1114255570Strasz TAILQ_FOREACH(lun, &targ->t_luns, l_next) { 1115263717Strasz error = conf_verify_lun(lun); 1116263717Strasz if (error != 0) 1117263717Strasz return (error); 1118265506Strasz found_lun = true; 1119255570Strasz } 1120265506Strasz if (!found_lun) { 1121265506Strasz log_warnx("no LUNs defined for target \"%s\"", 1122265506Strasz targ->t_name); 1123255570Strasz return (1); 1124255570Strasz } 1125255570Strasz } 1126255570Strasz TAILQ_FOREACH(pg, &conf->conf_portal_groups, pg_next) { 1127255570Strasz assert(pg->pg_name != NULL); 1128255570Strasz if (pg->pg_discovery_auth_group == NULL) { 1129255570Strasz pg->pg_discovery_auth_group = 1130263728Strasz auth_group_find(conf, "default"); 1131255570Strasz assert(pg->pg_discovery_auth_group != NULL); 1132255570Strasz } 1133255570Strasz 1134255570Strasz TAILQ_FOREACH(targ, &conf->conf_targets, t_next) { 1135255570Strasz if (targ->t_portal_group == pg) 1136255570Strasz break; 1137255570Strasz } 1138255570Strasz if (targ == NULL) { 1139255570Strasz if (strcmp(pg->pg_name, "default") != 0) 1140255570Strasz log_warnx("portal-group \"%s\" not assigned " 1141255570Strasz "to any target", pg->pg_name); 1142255570Strasz pg->pg_unassigned = true; 1143255570Strasz } else 1144255570Strasz pg->pg_unassigned = false; 1145255570Strasz } 1146255570Strasz TAILQ_FOREACH(ag, &conf->conf_auth_groups, ag_next) { 1147255570Strasz if (ag->ag_name == NULL) 1148255570Strasz assert(ag->ag_target != NULL); 1149255570Strasz else 1150255570Strasz assert(ag->ag_target == NULL); 1151255570Strasz 1152255570Strasz TAILQ_FOREACH(targ, &conf->conf_targets, t_next) { 1153255570Strasz if (targ->t_auth_group == ag) 1154255570Strasz break; 1155255570Strasz } 1156255570Strasz if (targ == NULL && ag->ag_name != NULL && 1157263728Strasz strcmp(ag->ag_name, "default") != 0 && 1158255570Strasz strcmp(ag->ag_name, "no-authentication") != 0 && 1159255570Strasz strcmp(ag->ag_name, "no-access") != 0) { 1160255570Strasz log_warnx("auth-group \"%s\" not assigned " 1161255570Strasz "to any target", ag->ag_name); 1162255570Strasz } 1163255570Strasz } 1164255570Strasz 1165255570Strasz return (0); 1166255570Strasz} 1167255570Strasz 1168255570Straszstatic int 1169255570Straszconf_apply(struct conf *oldconf, struct conf *newconf) 1170255570Strasz{ 1171255570Strasz struct target *oldtarg, *newtarg, *tmptarg; 1172255570Strasz struct lun *oldlun, *newlun, *tmplun; 1173255570Strasz struct portal_group *oldpg, *newpg; 1174255570Strasz struct portal *oldp, *newp; 1175255570Strasz pid_t otherpid; 1176255570Strasz int changed, cumulated_error = 0, error; 1177255570Strasz int one = 1; 1178255570Strasz 1179255570Strasz if (oldconf->conf_debug != newconf->conf_debug) { 1180255570Strasz log_debugx("changing debug level to %d", newconf->conf_debug); 1181255570Strasz log_init(newconf->conf_debug); 1182255570Strasz } 1183255570Strasz 1184255570Strasz if (oldconf->conf_pidfh != NULL) { 1185255570Strasz assert(oldconf->conf_pidfile_path != NULL); 1186255570Strasz if (newconf->conf_pidfile_path != NULL && 1187255570Strasz strcmp(oldconf->conf_pidfile_path, 1188255570Strasz newconf->conf_pidfile_path) == 0) { 1189255570Strasz newconf->conf_pidfh = oldconf->conf_pidfh; 1190255570Strasz oldconf->conf_pidfh = NULL; 1191255570Strasz } else { 1192255570Strasz log_debugx("removing pidfile %s", 1193255570Strasz oldconf->conf_pidfile_path); 1194255570Strasz pidfile_remove(oldconf->conf_pidfh); 1195255570Strasz oldconf->conf_pidfh = NULL; 1196255570Strasz } 1197255570Strasz } 1198255570Strasz 1199255570Strasz if (newconf->conf_pidfh == NULL && newconf->conf_pidfile_path != NULL) { 1200255570Strasz log_debugx("opening pidfile %s", newconf->conf_pidfile_path); 1201255570Strasz newconf->conf_pidfh = 1202255570Strasz pidfile_open(newconf->conf_pidfile_path, 0600, &otherpid); 1203255570Strasz if (newconf->conf_pidfh == NULL) { 1204255570Strasz if (errno == EEXIST) 1205255570Strasz log_errx(1, "daemon already running, pid: %jd.", 1206255570Strasz (intmax_t)otherpid); 1207255570Strasz log_err(1, "cannot open or create pidfile \"%s\"", 1208255570Strasz newconf->conf_pidfile_path); 1209255570Strasz } 1210255570Strasz } 1211255570Strasz 1212255570Strasz TAILQ_FOREACH_SAFE(oldtarg, &oldconf->conf_targets, t_next, tmptarg) { 1213255570Strasz /* 1214255570Strasz * First, remove any targets present in the old configuration 1215255570Strasz * and missing in the new one. 1216255570Strasz */ 1217263723Strasz newtarg = target_find(newconf, oldtarg->t_name); 1218255570Strasz if (newtarg == NULL) { 1219255570Strasz TAILQ_FOREACH_SAFE(oldlun, &oldtarg->t_luns, l_next, 1220255570Strasz tmplun) { 1221263716Strasz log_debugx("target %s not found in new " 1222263716Strasz "configuration; removing its lun %d, " 1223255570Strasz "backed by CTL lun %d", 1224263723Strasz oldtarg->t_name, oldlun->l_lun, 1225255570Strasz oldlun->l_ctl_lun); 1226255570Strasz error = kernel_lun_remove(oldlun); 1227255570Strasz if (error != 0) { 1228255570Strasz log_warnx("failed to remove lun %d, " 1229255570Strasz "target %s, CTL lun %d", 1230263723Strasz oldlun->l_lun, oldtarg->t_name, 1231255570Strasz oldlun->l_ctl_lun); 1232255570Strasz cumulated_error++; 1233255570Strasz } 1234255570Strasz lun_delete(oldlun); 1235255570Strasz } 1236255570Strasz target_delete(oldtarg); 1237255570Strasz continue; 1238255570Strasz } 1239255570Strasz 1240255570Strasz /* 1241255570Strasz * Second, remove any LUNs present in the old target 1242255570Strasz * and missing in the new one. 1243255570Strasz */ 1244255570Strasz TAILQ_FOREACH_SAFE(oldlun, &oldtarg->t_luns, l_next, tmplun) { 1245255570Strasz newlun = lun_find(newtarg, oldlun->l_lun); 1246255570Strasz if (newlun == NULL) { 1247255570Strasz log_debugx("lun %d, target %s, CTL lun %d " 1248263716Strasz "not found in new configuration; " 1249263723Strasz "removing", oldlun->l_lun, oldtarg->t_name, 1250255570Strasz oldlun->l_ctl_lun); 1251255570Strasz error = kernel_lun_remove(oldlun); 1252255570Strasz if (error != 0) { 1253255570Strasz log_warnx("failed to remove lun %d, " 1254255570Strasz "target %s, CTL lun %d", 1255263723Strasz oldlun->l_lun, oldtarg->t_name, 1256255570Strasz oldlun->l_ctl_lun); 1257255570Strasz cumulated_error++; 1258255570Strasz } 1259255570Strasz lun_delete(oldlun); 1260255570Strasz continue; 1261255570Strasz } 1262255570Strasz 1263255570Strasz /* 1264255570Strasz * Also remove the LUNs changed by more than size. 1265255570Strasz */ 1266255570Strasz changed = 0; 1267255570Strasz assert(oldlun->l_backend != NULL); 1268255570Strasz assert(newlun->l_backend != NULL); 1269255570Strasz if (strcmp(newlun->l_backend, oldlun->l_backend) != 0) { 1270255570Strasz log_debugx("backend for lun %d, target %s, " 1271255570Strasz "CTL lun %d changed; removing", 1272263723Strasz oldlun->l_lun, oldtarg->t_name, 1273255570Strasz oldlun->l_ctl_lun); 1274255570Strasz changed = 1; 1275255570Strasz } 1276255570Strasz if (oldlun->l_blocksize != newlun->l_blocksize) { 1277255570Strasz log_debugx("blocksize for lun %d, target %s, " 1278255570Strasz "CTL lun %d changed; removing", 1279263723Strasz oldlun->l_lun, oldtarg->t_name, 1280255570Strasz oldlun->l_ctl_lun); 1281255570Strasz changed = 1; 1282255570Strasz } 1283255570Strasz if (newlun->l_device_id != NULL && 1284255570Strasz (oldlun->l_device_id == NULL || 1285255570Strasz strcmp(oldlun->l_device_id, newlun->l_device_id) != 1286255570Strasz 0)) { 1287255570Strasz log_debugx("device-id for lun %d, target %s, " 1288255570Strasz "CTL lun %d changed; removing", 1289263723Strasz oldlun->l_lun, oldtarg->t_name, 1290255570Strasz oldlun->l_ctl_lun); 1291255570Strasz changed = 1; 1292255570Strasz } 1293255570Strasz if (newlun->l_path != NULL && 1294255570Strasz (oldlun->l_path == NULL || 1295255570Strasz strcmp(oldlun->l_path, newlun->l_path) != 0)) { 1296255570Strasz log_debugx("path for lun %d, target %s, " 1297255570Strasz "CTL lun %d, changed; removing", 1298263723Strasz oldlun->l_lun, oldtarg->t_name, 1299255570Strasz oldlun->l_ctl_lun); 1300255570Strasz changed = 1; 1301255570Strasz } 1302255570Strasz if (newlun->l_serial != NULL && 1303255570Strasz (oldlun->l_serial == NULL || 1304255570Strasz strcmp(oldlun->l_serial, newlun->l_serial) != 0)) { 1305255570Strasz log_debugx("serial for lun %d, target %s, " 1306255570Strasz "CTL lun %d changed; removing", 1307263723Strasz oldlun->l_lun, oldtarg->t_name, 1308255570Strasz oldlun->l_ctl_lun); 1309255570Strasz changed = 1; 1310255570Strasz } 1311255570Strasz if (changed) { 1312255570Strasz error = kernel_lun_remove(oldlun); 1313255570Strasz if (error != 0) { 1314255570Strasz log_warnx("failed to remove lun %d, " 1315255570Strasz "target %s, CTL lun %d", 1316263723Strasz oldlun->l_lun, oldtarg->t_name, 1317255570Strasz oldlun->l_ctl_lun); 1318255570Strasz cumulated_error++; 1319255570Strasz } 1320255570Strasz lun_delete(oldlun); 1321255570Strasz continue; 1322255570Strasz } 1323255570Strasz 1324255570Strasz lun_set_ctl_lun(newlun, oldlun->l_ctl_lun); 1325255570Strasz } 1326255570Strasz } 1327255570Strasz 1328255570Strasz /* 1329255570Strasz * Now add new targets or modify existing ones. 1330255570Strasz */ 1331255570Strasz TAILQ_FOREACH(newtarg, &newconf->conf_targets, t_next) { 1332263723Strasz oldtarg = target_find(oldconf, newtarg->t_name); 1333255570Strasz 1334255570Strasz TAILQ_FOREACH(newlun, &newtarg->t_luns, l_next) { 1335255570Strasz if (oldtarg != NULL) { 1336255570Strasz oldlun = lun_find(oldtarg, newlun->l_lun); 1337255570Strasz if (oldlun != NULL) { 1338255570Strasz if (newlun->l_size != oldlun->l_size) { 1339255570Strasz log_debugx("resizing lun %d, " 1340255570Strasz "target %s, CTL lun %d", 1341255570Strasz newlun->l_lun, 1342263723Strasz newtarg->t_name, 1343255570Strasz newlun->l_ctl_lun); 1344255570Strasz error = 1345255570Strasz kernel_lun_resize(newlun); 1346255570Strasz if (error != 0) { 1347255570Strasz log_warnx("failed to " 1348255570Strasz "resize lun %d, " 1349255570Strasz "target %s, " 1350255570Strasz "CTL lun %d", 1351255570Strasz newlun->l_lun, 1352263723Strasz newtarg->t_name, 1353255570Strasz newlun->l_lun); 1354255570Strasz cumulated_error++; 1355255570Strasz } 1356255570Strasz } 1357255570Strasz continue; 1358255570Strasz } 1359255570Strasz } 1360255570Strasz log_debugx("adding lun %d, target %s", 1361263723Strasz newlun->l_lun, newtarg->t_name); 1362255570Strasz error = kernel_lun_add(newlun); 1363255570Strasz if (error != 0) { 1364255570Strasz log_warnx("failed to add lun %d, target %s", 1365263723Strasz newlun->l_lun, newtarg->t_name); 1366255570Strasz cumulated_error++; 1367255570Strasz } 1368255570Strasz } 1369255570Strasz } 1370255570Strasz 1371255570Strasz /* 1372255570Strasz * Go through the new portals, opening the sockets as neccessary. 1373255570Strasz */ 1374255570Strasz TAILQ_FOREACH(newpg, &newconf->conf_portal_groups, pg_next) { 1375255570Strasz if (newpg->pg_unassigned) { 1376255570Strasz log_debugx("not listening on portal-group \"%s\", " 1377255570Strasz "not assigned to any target", 1378255570Strasz newpg->pg_name); 1379255570Strasz continue; 1380255570Strasz } 1381255570Strasz TAILQ_FOREACH(newp, &newpg->pg_portals, p_next) { 1382255570Strasz /* 1383255570Strasz * Try to find already open portal and reuse 1384255570Strasz * the listening socket. We don't care about 1385255570Strasz * what portal or portal group that was, what 1386255570Strasz * matters is the listening address. 1387255570Strasz */ 1388255570Strasz TAILQ_FOREACH(oldpg, &oldconf->conf_portal_groups, 1389255570Strasz pg_next) { 1390255570Strasz TAILQ_FOREACH(oldp, &oldpg->pg_portals, 1391255570Strasz p_next) { 1392255570Strasz if (strcmp(newp->p_listen, 1393255570Strasz oldp->p_listen) == 0 && 1394255570Strasz oldp->p_socket > 0) { 1395255570Strasz newp->p_socket = 1396255570Strasz oldp->p_socket; 1397255570Strasz oldp->p_socket = 0; 1398255570Strasz break; 1399255570Strasz } 1400255570Strasz } 1401255570Strasz } 1402255570Strasz if (newp->p_socket > 0) { 1403255570Strasz /* 1404255570Strasz * We're done with this portal. 1405255570Strasz */ 1406255570Strasz continue; 1407255570Strasz } 1408255570Strasz 1409255570Strasz#ifdef ICL_KERNEL_PROXY 1410265507Strasz if (proxy_mode) { 1411265509Strasz newpg->pg_conf->conf_portal_id++; 1412265509Strasz newp->p_id = newpg->pg_conf->conf_portal_id; 1413265509Strasz log_debugx("listening on %s, portal-group " 1414265509Strasz "\"%s\", portal id %d, using ICL proxy", 1415265509Strasz newp->p_listen, newpg->pg_name, newp->p_id); 1416265509Strasz kernel_listen(newp->p_ai, newp->p_iser, 1417265509Strasz newp->p_id); 1418265507Strasz continue; 1419265507Strasz } 1420265507Strasz#endif 1421265507Strasz assert(proxy_mode == false); 1422255570Strasz assert(newp->p_iser == false); 1423255570Strasz 1424255570Strasz log_debugx("listening on %s, portal-group \"%s\"", 1425255570Strasz newp->p_listen, newpg->pg_name); 1426255570Strasz newp->p_socket = socket(newp->p_ai->ai_family, 1427255570Strasz newp->p_ai->ai_socktype, 1428255570Strasz newp->p_ai->ai_protocol); 1429255570Strasz if (newp->p_socket < 0) { 1430255570Strasz log_warn("socket(2) failed for %s", 1431255570Strasz newp->p_listen); 1432255570Strasz cumulated_error++; 1433255570Strasz continue; 1434255570Strasz } 1435255570Strasz error = setsockopt(newp->p_socket, SOL_SOCKET, 1436255570Strasz SO_REUSEADDR, &one, sizeof(one)); 1437255570Strasz if (error != 0) { 1438255570Strasz log_warn("setsockopt(SO_REUSEADDR) failed " 1439255570Strasz "for %s", newp->p_listen); 1440255570Strasz close(newp->p_socket); 1441255570Strasz newp->p_socket = 0; 1442255570Strasz cumulated_error++; 1443255570Strasz continue; 1444255570Strasz } 1445255570Strasz error = bind(newp->p_socket, newp->p_ai->ai_addr, 1446255570Strasz newp->p_ai->ai_addrlen); 1447255570Strasz if (error != 0) { 1448255570Strasz log_warn("bind(2) failed for %s", 1449255570Strasz newp->p_listen); 1450255570Strasz close(newp->p_socket); 1451255570Strasz newp->p_socket = 0; 1452255570Strasz cumulated_error++; 1453255570Strasz continue; 1454255570Strasz } 1455255570Strasz error = listen(newp->p_socket, -1); 1456255570Strasz if (error != 0) { 1457255570Strasz log_warn("listen(2) failed for %s", 1458255570Strasz newp->p_listen); 1459255570Strasz close(newp->p_socket); 1460255570Strasz newp->p_socket = 0; 1461255570Strasz cumulated_error++; 1462255570Strasz continue; 1463255570Strasz } 1464255570Strasz } 1465255570Strasz } 1466255570Strasz 1467255570Strasz /* 1468255570Strasz * Go through the no longer used sockets, closing them. 1469255570Strasz */ 1470255570Strasz TAILQ_FOREACH(oldpg, &oldconf->conf_portal_groups, pg_next) { 1471255570Strasz TAILQ_FOREACH(oldp, &oldpg->pg_portals, p_next) { 1472255570Strasz if (oldp->p_socket <= 0) 1473255570Strasz continue; 1474255570Strasz log_debugx("closing socket for %s, portal-group \"%s\"", 1475255570Strasz oldp->p_listen, oldpg->pg_name); 1476255570Strasz close(oldp->p_socket); 1477255570Strasz oldp->p_socket = 0; 1478255570Strasz } 1479255570Strasz } 1480255570Strasz 1481255570Strasz return (cumulated_error); 1482255570Strasz} 1483255570Strasz 1484255570Straszbool 1485255570Strasztimed_out(void) 1486255570Strasz{ 1487255570Strasz 1488255570Strasz return (sigalrm_received); 1489255570Strasz} 1490255570Strasz 1491255570Straszstatic void 1492255570Straszsigalrm_handler(int dummy __unused) 1493255570Strasz{ 1494255570Strasz /* 1495255570Strasz * It would be easiest to just log an error and exit. We can't 1496255570Strasz * do this, though, because log_errx() is not signal safe, since 1497255570Strasz * it calls syslog(3). Instead, set a flag checked by pdu_send() 1498255570Strasz * and pdu_receive(), to call log_errx() there. Should they fail 1499255570Strasz * to notice, we'll exit here one second later. 1500255570Strasz */ 1501255570Strasz if (sigalrm_received) { 1502255570Strasz /* 1503255570Strasz * Oh well. Just give up and quit. 1504255570Strasz */ 1505255570Strasz _exit(2); 1506255570Strasz } 1507255570Strasz 1508255570Strasz sigalrm_received = true; 1509255570Strasz} 1510255570Strasz 1511255570Straszstatic void 1512255570Straszset_timeout(const struct conf *conf) 1513255570Strasz{ 1514255570Strasz struct sigaction sa; 1515255570Strasz struct itimerval itv; 1516255570Strasz int error; 1517255570Strasz 1518255570Strasz if (conf->conf_timeout <= 0) { 1519255570Strasz log_debugx("session timeout disabled"); 1520255570Strasz return; 1521255570Strasz } 1522255570Strasz 1523255570Strasz bzero(&sa, sizeof(sa)); 1524255570Strasz sa.sa_handler = sigalrm_handler; 1525255570Strasz sigfillset(&sa.sa_mask); 1526255570Strasz error = sigaction(SIGALRM, &sa, NULL); 1527255570Strasz if (error != 0) 1528255570Strasz log_err(1, "sigaction"); 1529255570Strasz 1530255570Strasz /* 1531255570Strasz * First SIGALRM will arive after conf_timeout seconds. 1532255570Strasz * If we do nothing, another one will arrive a second later. 1533255570Strasz */ 1534255570Strasz bzero(&itv, sizeof(itv)); 1535255570Strasz itv.it_interval.tv_sec = 1; 1536255570Strasz itv.it_value.tv_sec = conf->conf_timeout; 1537255570Strasz 1538255570Strasz log_debugx("setting session timeout to %d seconds", 1539255570Strasz conf->conf_timeout); 1540255570Strasz error = setitimer(ITIMER_REAL, &itv, NULL); 1541255570Strasz if (error != 0) 1542255570Strasz log_err(1, "setitimer"); 1543255570Strasz} 1544255570Strasz 1545255570Straszstatic int 1546255570Straszwait_for_children(bool block) 1547255570Strasz{ 1548255570Strasz pid_t pid; 1549255570Strasz int status; 1550255570Strasz int num = 0; 1551255570Strasz 1552255570Strasz for (;;) { 1553255570Strasz /* 1554255570Strasz * If "block" is true, wait for at least one process. 1555255570Strasz */ 1556255570Strasz if (block && num == 0) 1557255570Strasz pid = wait4(-1, &status, 0, NULL); 1558255570Strasz else 1559255570Strasz pid = wait4(-1, &status, WNOHANG, NULL); 1560255570Strasz if (pid <= 0) 1561255570Strasz break; 1562255570Strasz if (WIFSIGNALED(status)) { 1563255570Strasz log_warnx("child process %d terminated with signal %d", 1564255570Strasz pid, WTERMSIG(status)); 1565255570Strasz } else if (WEXITSTATUS(status) != 0) { 1566255570Strasz log_warnx("child process %d terminated with exit status %d", 1567255570Strasz pid, WEXITSTATUS(status)); 1568255570Strasz } else { 1569255570Strasz log_debugx("child process %d terminated gracefully", pid); 1570255570Strasz } 1571255570Strasz num++; 1572255570Strasz } 1573255570Strasz 1574255570Strasz return (num); 1575255570Strasz} 1576255570Strasz 1577255570Straszstatic void 1578255570Straszhandle_connection(struct portal *portal, int fd, bool dont_fork) 1579255570Strasz{ 1580255570Strasz struct connection *conn; 1581255570Strasz struct sockaddr_storage ss; 1582255570Strasz socklen_t sslen = sizeof(ss); 1583255570Strasz int error; 1584255570Strasz pid_t pid; 1585255570Strasz char host[NI_MAXHOST + 1]; 1586255570Strasz struct conf *conf; 1587255570Strasz 1588255570Strasz conf = portal->p_portal_group->pg_conf; 1589255570Strasz 1590255570Strasz if (dont_fork) { 1591255570Strasz log_debugx("incoming connection; not forking due to -d flag"); 1592255570Strasz } else { 1593255570Strasz nchildren -= wait_for_children(false); 1594255570Strasz assert(nchildren >= 0); 1595255570Strasz 1596255570Strasz while (conf->conf_maxproc > 0 && nchildren >= conf->conf_maxproc) { 1597255570Strasz log_debugx("maxproc limit of %d child processes hit; " 1598255570Strasz "waiting for child process to exit", conf->conf_maxproc); 1599255570Strasz nchildren -= wait_for_children(true); 1600255570Strasz assert(nchildren >= 0); 1601255570Strasz } 1602255570Strasz log_debugx("incoming connection; forking child process #%d", 1603255570Strasz nchildren); 1604255570Strasz nchildren++; 1605255570Strasz pid = fork(); 1606255570Strasz if (pid < 0) 1607255570Strasz log_err(1, "fork"); 1608255570Strasz if (pid > 0) { 1609255570Strasz close(fd); 1610255570Strasz return; 1611255570Strasz } 1612255570Strasz } 1613255570Strasz pidfile_close(conf->conf_pidfh); 1614255570Strasz 1615255570Strasz#ifdef ICL_KERNEL_PROXY 1616255570Strasz /* 1617255570Strasz * XXX 1618255570Strasz */ 1619265507Strasz if (proxy_mode) { 1620265507Strasz log_set_peer_addr("XXX"); 1621265507Strasz } else { 1622265507Strasz#endif 1623265507Strasz assert(proxy_mode == false); 1624265507Strasz error = getpeername(fd, (struct sockaddr *)&ss, &sslen); 1625265507Strasz if (error != 0) 1626265507Strasz log_err(1, "getpeername"); 1627265507Strasz error = getnameinfo((struct sockaddr *)&ss, sslen, 1628265507Strasz host, sizeof(host), NULL, 0, NI_NUMERICHOST); 1629265507Strasz if (error != 0) 1630265507Strasz log_errx(1, "getaddrinfo: %s", gai_strerror(error)); 1631255570Strasz 1632265507Strasz log_debugx("accepted connection from %s; portal group \"%s\"", 1633265507Strasz host, portal->p_portal_group->pg_name); 1634265507Strasz log_set_peer_addr(host); 1635265507Strasz setproctitle("%s", host); 1636265507Strasz#ifdef ICL_KERNEL_PROXY 1637265507Strasz } 1638255570Strasz#endif 1639255570Strasz 1640255570Strasz conn = connection_new(portal, fd, host); 1641255570Strasz set_timeout(conf); 1642255570Strasz kernel_capsicate(); 1643255570Strasz login(conn); 1644255570Strasz if (conn->conn_session_type == CONN_SESSION_TYPE_NORMAL) { 1645255570Strasz kernel_handoff(conn); 1646255570Strasz log_debugx("connection handed off to the kernel"); 1647255570Strasz } else { 1648255570Strasz assert(conn->conn_session_type == CONN_SESSION_TYPE_DISCOVERY); 1649255570Strasz discovery(conn); 1650255570Strasz } 1651255570Strasz log_debugx("nothing more to do; exiting"); 1652255570Strasz exit(0); 1653255570Strasz} 1654255570Strasz 1655255570Straszstatic int 1656255570Straszfd_add(int fd, fd_set *fdset, int nfds) 1657255570Strasz{ 1658255570Strasz 1659255570Strasz /* 1660255570Strasz * Skip sockets which we failed to bind. 1661255570Strasz */ 1662255570Strasz if (fd <= 0) 1663255570Strasz return (nfds); 1664255570Strasz 1665255570Strasz FD_SET(fd, fdset); 1666255570Strasz if (fd > nfds) 1667255570Strasz nfds = fd; 1668255570Strasz return (nfds); 1669255570Strasz} 1670255570Strasz 1671255570Straszstatic void 1672255570Straszmain_loop(struct conf *conf, bool dont_fork) 1673255570Strasz{ 1674255570Strasz struct portal_group *pg; 1675255570Strasz struct portal *portal; 1676255570Strasz#ifdef ICL_KERNEL_PROXY 1677255570Strasz int connection_id; 1678265509Strasz int portal_id; 1679265507Strasz#endif 1680255570Strasz fd_set fdset; 1681255570Strasz int error, nfds, client_fd; 1682255570Strasz 1683255570Strasz pidfile_write(conf->conf_pidfh); 1684255570Strasz 1685255570Strasz for (;;) { 1686255570Strasz if (sighup_received || sigterm_received) 1687255570Strasz return; 1688255570Strasz 1689255570Strasz#ifdef ICL_KERNEL_PROXY 1690265507Strasz if (proxy_mode) { 1691265509Strasz kernel_accept(&connection_id, &portal_id); 1692255570Strasz 1693265509Strasz log_debugx("incoming connection, id %d, portal id %d", 1694265509Strasz connection_id, portal_id); 1695265509Strasz TAILQ_FOREACH(pg, &conf->conf_portal_groups, pg_next) { 1696265509Strasz TAILQ_FOREACH(portal, &pg->pg_portals, p_next) { 1697265509Strasz if (portal->p_id == portal_id) { 1698265509Strasz goto found; 1699265509Strasz } 1700265509Strasz } 1701265509Strasz } 1702255570Strasz 1703265509Strasz log_errx(1, "kernel returned invalid portal_id %d", 1704265509Strasz portal_id); 1705265509Strasz 1706265509Straszfound: 1707265507Strasz handle_connection(portal, connection_id, dont_fork); 1708265507Strasz } else { 1709265507Strasz#endif 1710265507Strasz assert(proxy_mode == false); 1711265507Strasz 1712265507Strasz FD_ZERO(&fdset); 1713265507Strasz nfds = 0; 1714265507Strasz TAILQ_FOREACH(pg, &conf->conf_portal_groups, pg_next) { 1715265507Strasz TAILQ_FOREACH(portal, &pg->pg_portals, p_next) 1716265507Strasz nfds = fd_add(portal->p_socket, &fdset, nfds); 1717255570Strasz } 1718265507Strasz error = select(nfds + 1, &fdset, NULL, NULL, NULL); 1719265507Strasz if (error <= 0) { 1720265507Strasz if (errno == EINTR) 1721265507Strasz return; 1722265507Strasz log_err(1, "select"); 1723265507Strasz } 1724265507Strasz TAILQ_FOREACH(pg, &conf->conf_portal_groups, pg_next) { 1725265507Strasz TAILQ_FOREACH(portal, &pg->pg_portals, p_next) { 1726265507Strasz if (!FD_ISSET(portal->p_socket, &fdset)) 1727265507Strasz continue; 1728265507Strasz client_fd = accept(portal->p_socket, NULL, 0); 1729265507Strasz if (client_fd < 0) 1730265507Strasz log_err(1, "accept"); 1731265507Strasz handle_connection(portal, client_fd, dont_fork); 1732265507Strasz break; 1733265507Strasz } 1734265507Strasz } 1735265507Strasz#ifdef ICL_KERNEL_PROXY 1736255570Strasz } 1737265507Strasz#endif 1738255570Strasz } 1739255570Strasz} 1740255570Strasz 1741255570Straszstatic void 1742255570Straszsighup_handler(int dummy __unused) 1743255570Strasz{ 1744255570Strasz 1745255570Strasz sighup_received = true; 1746255570Strasz} 1747255570Strasz 1748255570Straszstatic void 1749255570Straszsigterm_handler(int dummy __unused) 1750255570Strasz{ 1751255570Strasz 1752255570Strasz sigterm_received = true; 1753255570Strasz} 1754255570Strasz 1755255570Straszstatic void 1756263730Straszsigchld_handler(int dummy __unused) 1757263730Strasz{ 1758263730Strasz 1759263730Strasz /* 1760263730Strasz * The only purpose of this handler is to make SIGCHLD 1761263730Strasz * interrupt the ISCSIDWAIT ioctl(2), so we can call 1762263730Strasz * wait_for_children(). 1763263730Strasz */ 1764263730Strasz} 1765263730Strasz 1766263730Straszstatic void 1767255570Straszregister_signals(void) 1768255570Strasz{ 1769255570Strasz struct sigaction sa; 1770255570Strasz int error; 1771255570Strasz 1772255570Strasz bzero(&sa, sizeof(sa)); 1773255570Strasz sa.sa_handler = sighup_handler; 1774255570Strasz sigfillset(&sa.sa_mask); 1775255570Strasz error = sigaction(SIGHUP, &sa, NULL); 1776255570Strasz if (error != 0) 1777255570Strasz log_err(1, "sigaction"); 1778255570Strasz 1779255570Strasz sa.sa_handler = sigterm_handler; 1780255570Strasz error = sigaction(SIGTERM, &sa, NULL); 1781255570Strasz if (error != 0) 1782255570Strasz log_err(1, "sigaction"); 1783255570Strasz 1784255570Strasz sa.sa_handler = sigterm_handler; 1785255570Strasz error = sigaction(SIGINT, &sa, NULL); 1786255570Strasz if (error != 0) 1787255570Strasz log_err(1, "sigaction"); 1788263730Strasz 1789263730Strasz sa.sa_handler = sigchld_handler; 1790263730Strasz error = sigaction(SIGCHLD, &sa, NULL); 1791263730Strasz if (error != 0) 1792263730Strasz log_err(1, "sigaction"); 1793255570Strasz} 1794255570Strasz 1795255570Straszint 1796255570Straszmain(int argc, char **argv) 1797255570Strasz{ 1798255570Strasz struct conf *oldconf, *newconf, *tmpconf; 1799255570Strasz const char *config_path = DEFAULT_CONFIG_PATH; 1800255570Strasz int debug = 0, ch, error; 1801255570Strasz bool dont_daemonize = false; 1802255570Strasz 1803265507Strasz while ((ch = getopt(argc, argv, "df:R")) != -1) { 1804255570Strasz switch (ch) { 1805255570Strasz case 'd': 1806255570Strasz dont_daemonize = true; 1807255570Strasz debug++; 1808255570Strasz break; 1809255570Strasz case 'f': 1810255570Strasz config_path = optarg; 1811255570Strasz break; 1812265507Strasz case 'R': 1813265507Strasz#ifndef ICL_KERNEL_PROXY 1814265507Strasz log_errx(1, "ctld(8) compiled without ICL_KERNEL_PROXY " 1815265507Strasz "does not support iSER protocol"); 1816265507Strasz#endif 1817265507Strasz proxy_mode = true; 1818265507Strasz break; 1819255570Strasz case '?': 1820255570Strasz default: 1821255570Strasz usage(); 1822255570Strasz } 1823255570Strasz } 1824255570Strasz argc -= optind; 1825255570Strasz if (argc != 0) 1826255570Strasz usage(); 1827255570Strasz 1828255570Strasz log_init(debug); 1829255570Strasz kernel_init(); 1830255570Strasz 1831255570Strasz oldconf = conf_new_from_kernel(); 1832255570Strasz newconf = conf_new_from_file(config_path); 1833255570Strasz if (newconf == NULL) 1834255570Strasz log_errx(1, "configuration error, exiting"); 1835255570Strasz if (debug > 0) { 1836255570Strasz oldconf->conf_debug = debug; 1837255570Strasz newconf->conf_debug = debug; 1838255570Strasz } 1839255570Strasz 1840255570Strasz log_debugx("enabling CTL iSCSI port"); 1841255570Strasz error = kernel_port_on(); 1842255570Strasz if (error != 0) 1843255570Strasz log_errx(1, "failed to enable CTL iSCSI port, exiting"); 1844255570Strasz 1845255570Strasz error = conf_apply(oldconf, newconf); 1846255570Strasz if (error != 0) 1847255570Strasz log_errx(1, "failed to apply configuration, exiting"); 1848255570Strasz conf_delete(oldconf); 1849255570Strasz oldconf = NULL; 1850255570Strasz 1851255570Strasz register_signals(); 1852255570Strasz 1853263719Strasz if (dont_daemonize == false) { 1854263719Strasz log_debugx("daemonizing"); 1855263719Strasz if (daemon(0, 0) == -1) { 1856263719Strasz log_warn("cannot daemonize"); 1857263719Strasz pidfile_remove(newconf->conf_pidfh); 1858263719Strasz exit(1); 1859263719Strasz } 1860263719Strasz } 1861263719Strasz 1862255570Strasz for (;;) { 1863255570Strasz main_loop(newconf, dont_daemonize); 1864255570Strasz if (sighup_received) { 1865255570Strasz sighup_received = false; 1866255570Strasz log_debugx("received SIGHUP, reloading configuration"); 1867255570Strasz tmpconf = conf_new_from_file(config_path); 1868255570Strasz if (tmpconf == NULL) { 1869255570Strasz log_warnx("configuration error, " 1870255570Strasz "continuing with old configuration"); 1871255570Strasz } else { 1872255570Strasz if (debug > 0) 1873255570Strasz tmpconf->conf_debug = debug; 1874255570Strasz oldconf = newconf; 1875255570Strasz newconf = tmpconf; 1876255570Strasz error = conf_apply(oldconf, newconf); 1877255570Strasz if (error != 0) 1878255570Strasz log_warnx("failed to reload " 1879255570Strasz "configuration"); 1880255570Strasz conf_delete(oldconf); 1881255570Strasz oldconf = NULL; 1882255570Strasz } 1883255570Strasz } else if (sigterm_received) { 1884255570Strasz log_debugx("exiting on signal; " 1885255570Strasz "reloading empty configuration"); 1886255570Strasz 1887255570Strasz log_debugx("disabling CTL iSCSI port " 1888255570Strasz "and terminating all connections"); 1889255570Strasz error = kernel_port_off(); 1890255570Strasz if (error != 0) 1891255570Strasz log_warnx("failed to disable CTL iSCSI port"); 1892255570Strasz 1893255570Strasz oldconf = newconf; 1894255570Strasz newconf = conf_new(); 1895255570Strasz if (debug > 0) 1896255570Strasz newconf->conf_debug = debug; 1897255570Strasz error = conf_apply(oldconf, newconf); 1898255570Strasz if (error != 0) 1899255570Strasz log_warnx("failed to apply configuration"); 1900255570Strasz 1901255570Strasz log_warnx("exiting on signal"); 1902255570Strasz exit(0); 1903255570Strasz } else { 1904255570Strasz nchildren -= wait_for_children(false); 1905255570Strasz assert(nchildren >= 0); 1906255570Strasz } 1907255570Strasz } 1908255570Strasz /* NOTREACHED */ 1909255570Strasz} 1910