1 /* $OpenBSD: iscsid.c,v 1.22 2021/04/16 14:37:06 claudio 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 extern 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 struct session_poll p = { 0 }; 215 int *valp; 216 217 cmh = pdu_getbuf(pdu, NULL, 0); 218 if (cmh == NULL) 219 goto done; 220 221 switch (cmh->type) { 222 case CTRL_INITIATOR_CONFIG: 223 if (cmh->len[0] != sizeof(*ic)) { 224 log_warnx("CTRL_INITIATOR_CONFIG bad size"); 225 control_compose(ch, CTRL_FAILURE, NULL, 0); 226 break; 227 } 228 ic = pdu_getbuf(pdu, NULL, 1); 229 memcpy(&initiator->config, ic, sizeof(initiator->config)); 230 control_compose(ch, CTRL_SUCCESS, NULL, 0); 231 break; 232 case CTRL_SESSION_CONFIG: 233 if (cmh->len[0] != sizeof(*sc)) { 234 log_warnx("CTRL_SESSION_CONFIG bad size"); 235 control_compose(ch, CTRL_FAILURE, NULL, 0); 236 break; 237 } 238 sc = pdu_getbuf(pdu, NULL, 1); 239 if (cmh->len[1]) 240 sc->TargetName = pdu_getbuf(pdu, NULL, 2); 241 else if (sc->SessionType != SESSION_TYPE_DISCOVERY) { 242 control_compose(ch, CTRL_FAILURE, NULL, 0); 243 goto done; 244 } else 245 sc->TargetName = NULL; 246 if (cmh->len[2]) 247 sc->InitiatorName = pdu_getbuf(pdu, NULL, 3); 248 else 249 sc->InitiatorName = NULL; 250 251 s = session_find(initiator, sc->SessionName); 252 if (s == NULL) { 253 s = session_new(initiator, sc->SessionType); 254 if (s == NULL) { 255 control_compose(ch, CTRL_FAILURE, NULL, 0); 256 goto done; 257 } 258 } 259 260 session_config(s, sc); 261 if (s->state == SESS_INIT) 262 session_fsm(s, SESS_EV_START, NULL, 0); 263 264 control_compose(ch, CTRL_SUCCESS, NULL, 0); 265 break; 266 case CTRL_LOG_VERBOSE: 267 if (cmh->len[0] != sizeof(int)) { 268 log_warnx("CTRL_LOG_VERBOSE bad size"); 269 control_compose(ch, CTRL_FAILURE, NULL, 0); 270 break; 271 } 272 valp = pdu_getbuf(pdu, NULL, 1); 273 log_verbose(*valp); 274 control_compose(ch, CTRL_SUCCESS, NULL, 0); 275 break; 276 case CTRL_VSCSI_STATS: 277 control_compose(ch, CTRL_VSCSI_STATS, vscsi_stats(), 278 sizeof(struct vscsi_stats)); 279 break; 280 case CTRL_SHOW_SUM: 281 control_compose(ch, CTRL_INITIATOR_CONFIG, &initiator->config, 282 sizeof(initiator->config)); 283 284 TAILQ_FOREACH(s, &initiator->sessions, entry) { 285 struct ctrldata cdv[3]; 286 bzero(cdv, sizeof(cdv)); 287 288 cdv[0].buf = &s->config; 289 cdv[0].len = sizeof(s->config); 290 291 if (s->config.TargetName) { 292 cdv[1].buf = s->config.TargetName; 293 cdv[1].len = 294 strlen(s->config.TargetName) + 1; 295 } 296 if (s->config.InitiatorName) { 297 cdv[2].buf = s->config.InitiatorName; 298 cdv[2].len = 299 strlen(s->config.InitiatorName) + 1; 300 } 301 302 control_build(ch, CTRL_SESSION_CONFIG, 303 nitems(cdv), cdv); 304 } 305 306 control_compose(ch, CTRL_SUCCESS, NULL, 0); 307 break; 308 case CTRL_SESS_POLL: 309 TAILQ_FOREACH(s, &initiator->sessions, entry) 310 poll_session(&p, s); 311 poll_finalize(&p); 312 control_compose(ch, CTRL_SESS_POLL, &p, sizeof(p)); 313 break; 314 default: 315 log_warnx("unknown control message type %d", cmh->type); 316 control_compose(ch, CTRL_FAILURE, NULL, 0); 317 break; 318 } 319 320 done: 321 pdu_free(pdu); 322 } 323 324 #define MERGE_MIN(r, a, b, v) \ 325 r->v = (a->v < b->v ? a->v : b->v) 326 #define MERGE_MAX(r, a, b, v) \ 327 r->v = (a->v > b->v ? a->v : b->v) 328 #define MERGE_OR(r, a, b, v) \ 329 r->v = (a->v || b->v) 330 #define MERGE_AND(r, a, b, v) \ 331 r->v = (a->v && b->v) 332 333 void 334 iscsi_merge_sess_params(struct session_params *res, 335 struct session_params *mine, struct session_params *his) 336 { 337 MERGE_MIN(res, mine, his, MaxBurstLength); 338 MERGE_MIN(res, mine, his, FirstBurstLength); 339 MERGE_MAX(res, mine, his, DefaultTime2Wait); 340 MERGE_MIN(res, mine, his, DefaultTime2Retain); 341 MERGE_MIN(res, mine, his, MaxOutstandingR2T); 342 res->TargetPortalGroupTag = his->TargetPortalGroupTag; 343 MERGE_MIN(res, mine, his, MaxConnections); 344 MERGE_OR(res, mine, his, InitialR2T); 345 MERGE_AND(res, mine, his, ImmediateData); 346 MERGE_OR(res, mine, his, DataPDUInOrder); 347 MERGE_OR(res, mine, his, DataSequenceInOrder); 348 MERGE_MIN(res, mine, his, ErrorRecoveryLevel); 349 350 } 351 352 void 353 iscsi_merge_conn_params(struct connection_params *res, 354 struct connection_params *mine, struct connection_params *his) 355 { 356 res->MaxRecvDataSegmentLength = his->MaxRecvDataSegmentLength; 357 /* XXX HeaderDigest and DataDigest */ 358 } 359 360 #undef MERGE_MIN 361 #undef MERGE_MAX 362 #undef MERGE_OR 363 #undef MERGE_AND 364