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