1 /* $OpenBSD: iscsid.c,v 1.20 2017/01/23 08:40:07 benno Exp $ */ 2 3 /* 4 * Copyright (c) 2009 Claudio Jeker <claudio@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/queue.h> 20 #include <sys/socket.h> 21 #include <sys/sysctl.h> 22 #include <sys/time.h> 23 #include <sys/uio.h> 24 25 #include <err.h> 26 #include <event.h> 27 #include <pwd.h> 28 #include <signal.h> 29 #include <stdio.h> 30 #include <stdlib.h> 31 #include <string.h> 32 #include <unistd.h> 33 34 #include "iscsid.h" 35 #include "log.h" 36 37 void main_sig_handler(int, short, void *); 38 __dead void usage(void); 39 void shutdown_cb(int, short, void *); 40 41 struct initiator *initiator; 42 struct event exit_ev; 43 int exit_rounds; 44 #define ISCSI_EXIT_WAIT 5 45 46 const struct session_params iscsi_sess_defaults = { 47 .MaxBurstLength = 262144, 48 .FirstBurstLength = 65536, 49 .DefaultTime2Wait = 2, 50 .DefaultTime2Retain = 20, 51 .MaxOutstandingR2T = 1, 52 .MaxConnections = 1, 53 .InitialR2T = 1, 54 .ImmediateData = 1, 55 .DataPDUInOrder = 1, 56 .DataSequenceInOrder = 1, 57 .ErrorRecoveryLevel = 0 58 }; 59 60 const struct connection_params iscsi_conn_defaults = { 61 .MaxRecvDataSegmentLength = 8192 62 }; 63 64 int 65 main(int argc, char *argv[]) 66 { 67 struct event ev_sigint, ev_sigterm, ev_sighup; 68 struct passwd *pw; 69 char *ctrlsock = ISCSID_CONTROL; 70 char *vscsidev = ISCSID_DEVICE; 71 int name[] = { CTL_KERN, KERN_PROC_NOBROADCASTKILL, 0 }; 72 int ch, debug = 0, verbose = 0, nobkill = 1; 73 74 log_procname = getprogname(); 75 76 log_init(1); /* log to stderr until daemonized */ 77 log_verbose(1); 78 79 while ((ch = getopt(argc, argv, "dn:s:v")) != -1) { 80 switch (ch) { 81 case 'd': 82 debug = 1; 83 break; 84 case 'n': 85 vscsidev = optarg; 86 break; 87 case 's': 88 ctrlsock = optarg; 89 break; 90 case 'v': 91 verbose = 1; 92 break; 93 default: 94 usage(); 95 /* NOTREACHED */ 96 } 97 } 98 99 argc -= optind; 100 argv += optind; 101 102 if (argc > 0) 103 usage(); 104 105 /* check for root privileges */ 106 if (geteuid()) 107 errx(1, "need root privileges"); 108 109 log_init(debug); 110 log_verbose(verbose); 111 112 if (control_init(ctrlsock) == -1) 113 fatalx("control socket setup failed"); 114 115 if (!debug) 116 daemon(1, 0); 117 log_info("startup"); 118 119 name[2] = getpid(); 120 if (sysctl(name, 3, NULL, 0, &nobkill, sizeof(nobkill)) != 0) 121 fatal("sysctl"); 122 123 event_init(); 124 vscsi_open(vscsidev); 125 126 /* chroot and drop to iscsid user */ 127 if ((pw = getpwnam(ISCSID_USER)) == NULL) 128 errx(1, "unknown user %s", ISCSID_USER); 129 130 if (chroot(pw->pw_dir) == -1) 131 fatal("chroot"); 132 if (chdir("/") == -1) 133 fatal("chdir(\"/\")"); 134 if (setgroups(1, &pw->pw_gid) || 135 setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || 136 setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) 137 fatal("can't drop privileges"); 138 139 /* setup signal handler */ 140 signal_set(&ev_sigint, SIGINT, main_sig_handler, NULL); 141 signal_set(&ev_sigterm, SIGTERM, main_sig_handler, NULL); 142 signal_set(&ev_sighup, SIGHUP, main_sig_handler, NULL); 143 signal_add(&ev_sigint, NULL); 144 signal_add(&ev_sigterm, NULL); 145 signal_add(&ev_sighup, NULL); 146 signal(SIGPIPE, SIG_IGN); 147 148 control_event_init(); 149 initiator = initiator_init(); 150 151 event_dispatch(); 152 153 /* do some cleanup on the way out */ 154 control_cleanup(ctrlsock); 155 initiator_cleanup(initiator); 156 log_info("exiting."); 157 return 0; 158 } 159 160 void 161 shutdown_cb(int fd, short event, void *arg) 162 { 163 struct timeval tv; 164 165 if (exit_rounds++ >= ISCSI_EXIT_WAIT || initiator_isdown(initiator)) 166 event_loopexit(NULL); 167 168 timerclear(&tv); 169 tv.tv_sec = 1; 170 171 if (evtimer_add(&exit_ev, &tv) == -1) 172 fatal("shutdown_cb"); 173 } 174 175 void 176 main_sig_handler(int sig, short event, void *arg) 177 { 178 struct timeval tv; 179 180 /* signal handler rules don't apply, libevent decouples for us */ 181 switch (sig) { 182 case SIGTERM: 183 case SIGINT: 184 case SIGHUP: 185 initiator_shutdown(initiator); 186 evtimer_set(&exit_ev, shutdown_cb, NULL); 187 timerclear(&tv); 188 if (evtimer_add(&exit_ev, &tv) == -1) 189 fatal("main_sig_handler"); 190 break; 191 default: 192 fatalx("unexpected signal"); 193 /* NOTREACHED */ 194 } 195 } 196 197 __dead void 198 usage(void) 199 { 200 extern char *__progname; 201 202 fprintf(stderr, "usage: %s [-dv] [-n device] [-s socket]\n", 203 __progname); 204 exit(1); 205 } 206 207 void 208 iscsid_ctrl_dispatch(void *ch, struct pdu *pdu) 209 { 210 struct ctrlmsghdr *cmh; 211 struct initiator_config *ic; 212 struct session_config *sc; 213 struct session *s; 214 int *valp; 215 216 cmh = pdu_getbuf(pdu, NULL, 0); 217 if (cmh == NULL) 218 goto done; 219 220 switch (cmh->type) { 221 case CTRL_INITIATOR_CONFIG: 222 if (cmh->len[0] != sizeof(*ic)) { 223 log_warnx("CTRL_INITIATOR_CONFIG bad size"); 224 control_compose(ch, CTRL_FAILURE, NULL, 0); 225 break; 226 } 227 ic = pdu_getbuf(pdu, NULL, 1); 228 memcpy(&initiator->config, ic, sizeof(initiator->config)); 229 control_compose(ch, CTRL_SUCCESS, NULL, 0); 230 break; 231 case CTRL_SESSION_CONFIG: 232 if (cmh->len[0] != sizeof(*sc)) { 233 log_warnx("CTRL_SESSION_CONFIG bad size"); 234 control_compose(ch, CTRL_FAILURE, NULL, 0); 235 break; 236 } 237 sc = pdu_getbuf(pdu, NULL, 1); 238 if (cmh->len[1]) 239 sc->TargetName = pdu_getbuf(pdu, NULL, 2); 240 else if (sc->SessionType != SESSION_TYPE_DISCOVERY) { 241 control_compose(ch, CTRL_FAILURE, NULL, 0); 242 goto done; 243 } else 244 sc->TargetName = NULL; 245 if (cmh->len[2]) 246 sc->InitiatorName = pdu_getbuf(pdu, NULL, 3); 247 else 248 sc->InitiatorName = NULL; 249 250 s = session_find(initiator, sc->SessionName); 251 if (s == NULL) { 252 s = session_new(initiator, sc->SessionType); 253 if (s == NULL) { 254 control_compose(ch, CTRL_FAILURE, NULL, 0); 255 goto done; 256 } 257 } 258 259 session_config(s, sc); 260 if (s->state == SESS_INIT) 261 session_fsm(s, SESS_EV_START, NULL, 0); 262 263 control_compose(ch, CTRL_SUCCESS, NULL, 0); 264 break; 265 case CTRL_LOG_VERBOSE: 266 if (cmh->len[0] != sizeof(int)) { 267 log_warnx("CTRL_LOG_VERBOSE bad size"); 268 control_compose(ch, CTRL_FAILURE, NULL, 0); 269 break; 270 } 271 valp = pdu_getbuf(pdu, NULL, 1); 272 log_verbose(*valp); 273 control_compose(ch, CTRL_SUCCESS, NULL, 0); 274 break; 275 case CTRL_VSCSI_STATS: 276 control_compose(ch, CTRL_VSCSI_STATS, vscsi_stats(), 277 sizeof(struct vscsi_stats)); 278 break; 279 case CTRL_SHOW_SUM: 280 control_compose(ch, CTRL_INITIATOR_CONFIG, &initiator->config, 281 sizeof(initiator->config)); 282 283 TAILQ_FOREACH(s, &initiator->sessions, entry) { 284 struct ctrldata cdv[3]; 285 bzero(cdv, sizeof(cdv)); 286 287 cdv[0].buf = &s->config; 288 cdv[0].len = sizeof(s->config); 289 290 if (s->config.TargetName) { 291 cdv[1].buf = s->config.TargetName; 292 cdv[1].len = 293 strlen(s->config.TargetName) + 1; 294 } 295 if (s->config.InitiatorName) { 296 cdv[2].buf = s->config.InitiatorName; 297 cdv[2].len = 298 strlen(s->config.InitiatorName) + 1; 299 } 300 301 control_build(ch, CTRL_SESSION_CONFIG, 302 nitems(cdv), cdv); 303 } 304 305 control_compose(ch, CTRL_SUCCESS, NULL, 0); 306 break; 307 default: 308 log_warnx("unknown control message type %d", cmh->type); 309 control_compose(ch, CTRL_FAILURE, NULL, 0); 310 break; 311 } 312 313 done: 314 pdu_free(pdu); 315 } 316 317 #define MERGE_MIN(r, a, b, v) \ 318 r->v = (a->v < b->v ? a->v : b->v) 319 #define MERGE_MAX(r, a, b, v) \ 320 r->v = (a->v > b->v ? a->v : b->v) 321 #define MERGE_OR(r, a, b, v) \ 322 r->v = (a->v || b->v) 323 #define MERGE_AND(r, a, b, v) \ 324 r->v = (a->v && b->v) 325 326 void 327 iscsi_merge_sess_params(struct session_params *res, 328 struct session_params *mine, struct session_params *his) 329 { 330 MERGE_MIN(res, mine, his, MaxBurstLength); 331 MERGE_MIN(res, mine, his, FirstBurstLength); 332 MERGE_MAX(res, mine, his, DefaultTime2Wait); 333 MERGE_MIN(res, mine, his, DefaultTime2Retain); 334 MERGE_MIN(res, mine, his, MaxOutstandingR2T); 335 res->TargetPortalGroupTag = his->TargetPortalGroupTag; 336 MERGE_MIN(res, mine, his, MaxConnections); 337 MERGE_OR(res, mine, his, InitialR2T); 338 MERGE_AND(res, mine, his, ImmediateData); 339 MERGE_OR(res, mine, his, DataPDUInOrder); 340 MERGE_OR(res, mine, his, DataSequenceInOrder); 341 MERGE_MIN(res, mine, his, ErrorRecoveryLevel); 342 343 } 344 345 void 346 iscsi_merge_conn_params(struct connection_params *res, 347 struct connection_params *mine, struct connection_params *his) 348 { 349 res->MaxRecvDataSegmentLength = his->MaxRecvDataSegmentLength; 350 /* XXX HeaderDigest and DataDigest */ 351 } 352 353 #undef MERGE_MIN 354 #undef MERGE_MAX 355 #undef MERGE_OR 356 #undef MERGE_AND 357