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