1 /* $OpenBSD: iscsid.c,v 1.19 2016/08/16 18:41:57 tedu 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_init(1); /* log to stderr until daemonized */ 75 log_verbose(1); 76 77 while ((ch = getopt(argc, argv, "dn:s:v")) != -1) { 78 switch (ch) { 79 case 'd': 80 debug = 1; 81 break; 82 case 'n': 83 vscsidev = optarg; 84 break; 85 case 's': 86 ctrlsock = optarg; 87 break; 88 case 'v': 89 verbose = 1; 90 break; 91 default: 92 usage(); 93 /* NOTREACHED */ 94 } 95 } 96 97 argc -= optind; 98 argv += optind; 99 100 if (argc > 0) 101 usage(); 102 103 /* check for root privileges */ 104 if (geteuid()) 105 errx(1, "need root privileges"); 106 107 log_init(debug); 108 log_verbose(verbose); 109 110 if (control_init(ctrlsock) == -1) 111 fatalx("control socket setup failed"); 112 113 if (!debug) 114 daemon(1, 0); 115 log_info("startup"); 116 117 name[2] = getpid(); 118 if (sysctl(name, 3, NULL, 0, &nobkill, sizeof(nobkill)) != 0) 119 fatal("sysctl"); 120 121 event_init(); 122 vscsi_open(vscsidev); 123 124 /* chroot and drop to iscsid user */ 125 if ((pw = getpwnam(ISCSID_USER)) == NULL) 126 errx(1, "unknown user %s", ISCSID_USER); 127 128 if (chroot(pw->pw_dir) == -1) 129 fatal("chroot"); 130 if (chdir("/") == -1) 131 fatal("chdir(\"/\")"); 132 if (setgroups(1, &pw->pw_gid) || 133 setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || 134 setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) 135 fatal("can't drop privileges"); 136 137 /* setup signal handler */ 138 signal_set(&ev_sigint, SIGINT, main_sig_handler, NULL); 139 signal_set(&ev_sigterm, SIGTERM, main_sig_handler, NULL); 140 signal_set(&ev_sighup, SIGHUP, main_sig_handler, NULL); 141 signal_add(&ev_sigint, NULL); 142 signal_add(&ev_sigterm, NULL); 143 signal_add(&ev_sighup, NULL); 144 signal(SIGPIPE, SIG_IGN); 145 146 control_event_init(); 147 initiator = initiator_init(); 148 149 event_dispatch(); 150 151 /* do some cleanup on the way out */ 152 control_cleanup(ctrlsock); 153 initiator_cleanup(initiator); 154 log_info("exiting."); 155 return 0; 156 } 157 158 void 159 shutdown_cb(int fd, short event, void *arg) 160 { 161 struct timeval tv; 162 163 if (exit_rounds++ >= ISCSI_EXIT_WAIT || initiator_isdown(initiator)) 164 event_loopexit(NULL); 165 166 timerclear(&tv); 167 tv.tv_sec = 1; 168 169 if (evtimer_add(&exit_ev, &tv) == -1) 170 fatal("shutdown_cb"); 171 } 172 173 void 174 main_sig_handler(int sig, short event, void *arg) 175 { 176 struct timeval tv; 177 178 /* signal handler rules don't apply, libevent decouples for us */ 179 switch (sig) { 180 case SIGTERM: 181 case SIGINT: 182 case SIGHUP: 183 initiator_shutdown(initiator); 184 evtimer_set(&exit_ev, shutdown_cb, NULL); 185 timerclear(&tv); 186 if (evtimer_add(&exit_ev, &tv) == -1) 187 fatal("main_sig_handler"); 188 break; 189 default: 190 fatalx("unexpected signal"); 191 /* NOTREACHED */ 192 } 193 } 194 195 __dead void 196 usage(void) 197 { 198 extern char *__progname; 199 200 fprintf(stderr, "usage: %s [-dv] [-n device] [-s socket]\n", 201 __progname); 202 exit(1); 203 } 204 205 void 206 iscsid_ctrl_dispatch(void *ch, struct pdu *pdu) 207 { 208 struct ctrlmsghdr *cmh; 209 struct initiator_config *ic; 210 struct session_config *sc; 211 struct session *s; 212 int *valp; 213 214 cmh = pdu_getbuf(pdu, NULL, 0); 215 if (cmh == NULL) 216 goto done; 217 218 switch (cmh->type) { 219 case CTRL_INITIATOR_CONFIG: 220 if (cmh->len[0] != sizeof(*ic)) { 221 log_warnx("CTRL_INITIATOR_CONFIG bad size"); 222 control_compose(ch, CTRL_FAILURE, NULL, 0); 223 break; 224 } 225 ic = pdu_getbuf(pdu, NULL, 1); 226 memcpy(&initiator->config, ic, sizeof(initiator->config)); 227 control_compose(ch, CTRL_SUCCESS, NULL, 0); 228 break; 229 case CTRL_SESSION_CONFIG: 230 if (cmh->len[0] != sizeof(*sc)) { 231 log_warnx("CTRL_SESSION_CONFIG bad size"); 232 control_compose(ch, CTRL_FAILURE, NULL, 0); 233 break; 234 } 235 sc = pdu_getbuf(pdu, NULL, 1); 236 if (cmh->len[1]) 237 sc->TargetName = pdu_getbuf(pdu, NULL, 2); 238 else if (sc->SessionType != SESSION_TYPE_DISCOVERY) { 239 control_compose(ch, CTRL_FAILURE, NULL, 0); 240 goto done; 241 } else 242 sc->TargetName = NULL; 243 if (cmh->len[2]) 244 sc->InitiatorName = pdu_getbuf(pdu, NULL, 3); 245 else 246 sc->InitiatorName = NULL; 247 248 s = session_find(initiator, sc->SessionName); 249 if (s == NULL) { 250 s = session_new(initiator, sc->SessionType); 251 if (s == NULL) { 252 control_compose(ch, CTRL_FAILURE, NULL, 0); 253 goto done; 254 } 255 } 256 257 session_config(s, sc); 258 if (s->state == SESS_INIT) 259 session_fsm(s, SESS_EV_START, NULL, 0); 260 261 control_compose(ch, CTRL_SUCCESS, NULL, 0); 262 break; 263 case CTRL_LOG_VERBOSE: 264 if (cmh->len[0] != sizeof(int)) { 265 log_warnx("CTRL_LOG_VERBOSE bad size"); 266 control_compose(ch, CTRL_FAILURE, NULL, 0); 267 break; 268 } 269 valp = pdu_getbuf(pdu, NULL, 1); 270 log_verbose(*valp); 271 control_compose(ch, CTRL_SUCCESS, NULL, 0); 272 break; 273 case CTRL_VSCSI_STATS: 274 control_compose(ch, CTRL_VSCSI_STATS, vscsi_stats(), 275 sizeof(struct vscsi_stats)); 276 break; 277 case CTRL_SHOW_SUM: 278 control_compose(ch, CTRL_INITIATOR_CONFIG, &initiator->config, 279 sizeof(initiator->config)); 280 281 TAILQ_FOREACH(s, &initiator->sessions, entry) { 282 struct ctrldata cdv[3]; 283 bzero(cdv, sizeof(cdv)); 284 285 cdv[0].buf = &s->config; 286 cdv[0].len = sizeof(s->config); 287 288 if (s->config.TargetName) { 289 cdv[1].buf = s->config.TargetName; 290 cdv[1].len = 291 strlen(s->config.TargetName) + 1; 292 } 293 if (s->config.InitiatorName) { 294 cdv[2].buf = s->config.InitiatorName; 295 cdv[2].len = 296 strlen(s->config.InitiatorName) + 1; 297 } 298 299 control_build(ch, CTRL_SESSION_CONFIG, 300 nitems(cdv), cdv); 301 } 302 303 control_compose(ch, CTRL_SUCCESS, NULL, 0); 304 break; 305 default: 306 log_warnx("unknown control message type %d", cmh->type); 307 control_compose(ch, CTRL_FAILURE, NULL, 0); 308 break; 309 } 310 311 done: 312 pdu_free(pdu); 313 } 314 315 #define MERGE_MIN(r, a, b, v) \ 316 r->v = (a->v < b->v ? a->v : b->v) 317 #define MERGE_MAX(r, a, b, v) \ 318 r->v = (a->v > b->v ? a->v : b->v) 319 #define MERGE_OR(r, a, b, v) \ 320 r->v = (a->v || b->v) 321 #define MERGE_AND(r, a, b, v) \ 322 r->v = (a->v && b->v) 323 324 void 325 iscsi_merge_sess_params(struct session_params *res, 326 struct session_params *mine, struct session_params *his) 327 { 328 MERGE_MIN(res, mine, his, MaxBurstLength); 329 MERGE_MIN(res, mine, his, FirstBurstLength); 330 MERGE_MAX(res, mine, his, DefaultTime2Wait); 331 MERGE_MIN(res, mine, his, DefaultTime2Retain); 332 MERGE_MIN(res, mine, his, MaxOutstandingR2T); 333 res->TargetPortalGroupTag = his->TargetPortalGroupTag; 334 MERGE_MIN(res, mine, his, MaxConnections); 335 MERGE_OR(res, mine, his, InitialR2T); 336 MERGE_AND(res, mine, his, ImmediateData); 337 MERGE_OR(res, mine, his, DataPDUInOrder); 338 MERGE_OR(res, mine, his, DataSequenceInOrder); 339 MERGE_MIN(res, mine, his, ErrorRecoveryLevel); 340 341 } 342 343 void 344 iscsi_merge_conn_params(struct connection_params *res, 345 struct connection_params *mine, struct connection_params *his) 346 { 347 res->MaxRecvDataSegmentLength = his->MaxRecvDataSegmentLength; 348 /* XXX HeaderDigest and DataDigest */ 349 } 350 351 #undef MERGE_MIN 352 #undef MERGE_MAX 353 #undef MERGE_OR 354 #undef MERGE_AND 355