common.c revision 131823
1/* 2 * Copyright (c) 2001-2003 3 * Fraunhofer Institute for Open Communication Systems (FhG Fokus). 4 * All rights reserved. 5 * 6 * Author: Harti Brandt <harti@freebsd.org> 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 * 29 * $Begemot: libunimsg/sscop/common.c,v 1.4 2004/07/08 08:22:27 brandt Exp $ 30 */ 31 32#include <sys/types.h> 33#include <sys/socket.h> 34#include <sys/uio.h> 35 36#include <stdio.h> 37#include <stdlib.h> 38#include <stdarg.h> 39#include <string.h> 40#include <errno.h> 41#include <unistd.h> 42#include <time.h> 43#include <signal.h> 44#include <assert.h> 45#include <fcntl.h> 46#include <err.h> 47#include <isc/eventlib.h> 48 49#include <netnatm/unimsg.h> 50#include <netnatm/saal/sscop.h> 51#include "common.h" 52 53struct timer { 54 evTimerID id; 55 struct sscop *sscop; 56 void (*func)(void *); 57}; 58 59int useframe; 60int sscopframe; 61u_int sscop_vflag; 62int sscop_fd; 63int user_fd; 64int loose; 65int user_out_fd; 66u_int verbose; 67evContext evctx; 68evFileID sscop_h; 69evFileID user_h; 70 71/* 72 * This function get's called from sscop to put out verbose messages 73 */ 74void 75sscop_verbose(struct sscop *sscop __unused, void *u __unused, 76 const char *fmt, ...) 77{ 78 va_list ap; 79 80 va_start(ap, fmt); 81 vfprintf(stderr, fmt, ap); 82 fprintf(stderr, "\n"); 83 va_end(ap); 84} 85void 86verb(const char *fmt, ...) 87{ 88 va_list ap; 89 90 va_start(ap, fmt); 91 vfprintf(stderr, fmt, ap); 92 fprintf(stderr, "\n"); 93 va_end(ap); 94} 95 96/* 97 * Dump a buffer in hex to stderr. 98 */ 99void 100dump_buf(const char *w, const u_char *buf, size_t len) 101{ 102 u_int i; 103 104 fprintf(stderr, "%s %zu: ", w, len); 105 for(i = 0; i < len; i++) { 106 if (i % 4 == 0 && i != 0) 107 fprintf(stderr, " "); 108 fprintf(stderr, "%02x", *buf++); 109 } 110 fprintf(stderr, "\n"); 111} 112 113/* 114 * SSCOP file descriptor is ready. Allocate and read one message 115 * and dispatch a signal. 116 */ 117struct uni_msg * 118proto_msgin(int fd __unused) 119{ 120 struct uni_msg *m = NULL; 121 ssize_t size; 122 u_int32_t flen; 123 u_int got; 124 125 if (sscopframe) { 126 if ((size = read(sscop_fd, &flen, 4)) == -1) 127 err(1, "error reading frame hdr"); 128 if (size == 0) { 129 got = 0; 130 goto eof; 131 } 132 if (size != 4) 133 errx(1, "short frame header: %zd", size); 134 if ((m = uni_msg_alloc(flen)) == NULL) 135 err(1, NULL); 136 for (got = 0; got < flen; got += (size_t)size) { 137 size = read(sscop_fd, m->b_rptr + got, flen - got); 138 if (size == -1) 139 err(1, "error reading frame"); 140 if (size == 0) { 141 got = 0; 142 break; 143 } 144 } 145 146 } else { 147 if ((m = uni_msg_alloc(MAXMSG)) == NULL) 148 err(1, NULL); 149 if ((size = read(sscop_fd, m->b_rptr, MAXMSG)) == -1) 150 err(1, "error reading message"); 151 got = size; 152 } 153 154 if (got == 0) { 155 eof: 156 evDeselectFD(evctx, sscop_h); 157 (void)close(sscop_fd); 158 sscop_fd = -1; 159 if (m != NULL) 160 uni_msg_destroy(m); 161 VERBOSE(("EOF on sscop file descriptor")); 162 return (NULL); 163 } 164 m->b_wptr = m->b_rptr + got; 165 166 if(verbose & 0x0002) 167 dump_buf("SSCOP INPUT", m->b_rptr, got); 168 169 return (m); 170} 171 172/* 173 * User file descriptor ready - read a message 174 */ 175struct uni_msg * 176user_msgin(int fd __unused) 177{ 178 struct uni_msg *m = NULL; 179 ssize_t size; 180 u_int32_t flen; 181 u_int got; 182 183 if (useframe) { 184 if ((size = read(user_fd, &flen, 4)) == -1) 185 err(1, "error reading frame hdr"); 186 if (size == 0) { 187 got = 0; 188 goto eof; 189 } 190 if (size != 4) 191 errx(1, "short frame header: %zd", size); 192 if ((m = uni_msg_alloc(flen)) == NULL) 193 err(1, NULL); 194 for (got = 0; got < flen; got++) { 195 size = read(user_fd, m->b_rptr + got, flen - got); 196 if (size == -1) 197 err(1, "error reading frame"); 198 if (size == 0) { 199 got = 0; 200 break; 201 } 202 got += (size_t)size; 203 } 204 205 } else { 206 if ((m = uni_msg_alloc(MAXMSG)) == NULL) 207 err(1, NULL); 208 if ((size = read(user_fd, m->b_rptr, MAXMSG)) == -1) 209 err(1, "error reading message"); 210 got = size; 211 } 212 213 if (size == 0) { 214 eof: 215 evDeselectFD(evctx, user_h); 216 if (m != NULL) 217 uni_msg_destroy(m); 218 VERBOSE(("EOF on user connection")); 219 return (NULL); 220 } 221 m->b_wptr = m->b_rptr + size; 222 223 return (m); 224} 225 226/* 227 * Write message to the SSCOP file descriptor. 228 * Here we have a problem: we should have a means to check how much space 229 * we have. If the pipe is full, we could declare the lower layer busy and 230 * drop the message. However, how do we know, when a message will fit? 231 * Selecting for WRITE doesn't help, because it will return even if a single 232 * byte can be written. For this reason, we switch the file descriptor to 233 * blocking mode, and hope everything is fast enough to not timeout us. 234 * Alternatively we could just drop the message. Using kevent would help here. 235 */ 236void 237proto_msgout(struct uni_msg *m) 238{ 239 struct iovec iov[2]; 240 u_int32_t flen; 241 ssize_t size; 242 static int sent; 243 int fl; 244 245 if (verbose & 0x0002) 246 dump_buf("send", m->b_rptr, uni_msg_len(m)); 247 if (loose > 0 && (sent++ % loose) == loose - 1) { 248 VERBOSE(("loosing message")); 249 uni_msg_destroy(m); 250 return; 251 } 252 253 flen = uni_msg_len(m); 254 255 iov[0].iov_len = sscopframe ? 4 : 0; 256 iov[0].iov_base = (caddr_t)&flen; 257 iov[1].iov_len = uni_msg_len(m); 258 iov[1].iov_base = m->b_rptr; 259 260 if ((fl = fcntl(sscop_fd, F_GETFL, 0)) == -1) 261 err(1, "cannot get flags for sscop fd"); 262 fl &= ~O_NONBLOCK; 263 if (fcntl(sscop_fd, F_SETFL, fl) == -1) 264 err(1, "cannot set flags for sscop fd"); 265 266 if ((size = writev(sscop_fd, iov, 2)) == -1) 267 err(1, "write sscop"); 268 if ((size_t)size != iov[0].iov_len + iov[1].iov_len) 269 err(1, "short sscop write %zu %zu %zd", 270 iov[0].iov_len, iov[1].iov_len, size); 271 272 fl |= O_NONBLOCK; 273 if (fcntl(sscop_fd, F_SETFL, fl) == -1) 274 err(1, "cannot restore flags for sscop fd"); 275 276 uni_msg_destroy(m); 277} 278 279/* 280 * output a message to the user 281 */ 282void 283user_msgout(struct uni_msg *m) 284{ 285 struct iovec iov[2]; 286 u_int32_t flen; 287 ssize_t size; 288 289 flen = uni_msg_len(m); 290 291 iov[0].iov_len = useframe ? 4 : 0; 292 iov[0].iov_base = (caddr_t)&flen; 293 iov[1].iov_len = uni_msg_len(m); 294 iov[1].iov_base = m->b_rptr; 295 296 if ((size = writev(user_out_fd, iov, 2)) == -1) 297 err(1, "write sscop"); 298 if ((size_t)size != iov[0].iov_len + iov[1].iov_len) 299 errx(1, "short sscop write"); 300 301 uni_msg_destroy(m); 302} 303 304void 305parse_param(struct sscop_param *param, u_int *pmask, int opt, char *arg) 306{ 307 u_int val; 308 char *end, *p; 309 310 if(opt == 'b') { 311 param->flags |= SSCOP_ROBUST; 312 *pmask |= SSCOP_SET_ROBUST; 313 return; 314 } 315 if(opt == 'x') { 316 param->flags |= SSCOP_POLLREX; 317 *pmask |= SSCOP_SET_POLLREX; 318 return; 319 } 320 if(opt == 'W') { 321 val = (u_int)strtoul(optarg, &end, 0); 322 323 if(*end != '\0') 324 errx(1, "bad number to -W '%s'", optarg); 325 if(val >= (1 << 24) - 1) 326 errx(1, "window too large: 0x%x", val); 327 param->mr = val; 328 *pmask |= SSCOP_SET_MR; 329 return; 330 } 331 332 if((p = strchr(arg, '=')) == NULL) 333 errx(1, "need '=' in argument to -%c", opt); 334 *p++ = 0; 335 if(*p == 0) 336 errx(1, "argument to -%c %s empty", opt, arg); 337 val = strtoul(p, &end, 0); 338 if(*end != 0) 339 errx(1, "bad number in -%c %s=%s", opt, arg, p); 340 341 if(opt == 't') { 342 if(strcmp(arg, "cc") == 0) { 343 param->timer_cc = val; 344 *pmask |= SSCOP_SET_TCC; 345 } else if(strcmp(arg, "poll") == 0) { 346 param->timer_poll = val; 347 *pmask |= SSCOP_SET_TPOLL; 348 } else if(strcmp(arg, "ka") == 0) { 349 param->timer_keep_alive = val; 350 *pmask |= SSCOP_SET_TKA; 351 } else if(strcmp(arg, "nr") == 0) { 352 param->timer_no_response = val; 353 *pmask |= SSCOP_SET_TNR; 354 } else if(strcmp(arg, "idle") == 0) { 355 param->timer_idle = val; 356 *pmask |= SSCOP_SET_TIDLE; 357 } else 358 errx(1, "bad timer name '%s'", arg); 359 return; 360 } 361 362 if(opt == 'a') { 363 if(strcmp(arg, "j") == 0) { 364 param->maxj = val; 365 *pmask |= SSCOP_SET_MAXJ; 366 } else if(strcmp(arg, "k") == 0) { 367 param->maxk = val; 368 *pmask |= SSCOP_SET_MAXK; 369 } else if(strcmp(arg, "cc") == 0) { 370 param->maxcc = val; 371 *pmask |= SSCOP_SET_MAXCC; 372 } else if(strcmp(arg, "pd") == 0) { 373 param->maxpd = val; 374 *pmask |= SSCOP_SET_MAXPD; 375 } else if(strcmp(arg, "stat") == 0) { 376 param->maxstat = val; 377 *pmask |= SSCOP_SET_MAXSTAT; 378 } else 379 errx(1, "bad parameter '%s'", arg); 380 return; 381 } 382 383 verb("bad flag"); 384 abort(); 385} 386 387static void 388tfunc(evContext ctx __unused, void *uap, struct timespec due __unused, 389 struct timespec inter __unused) 390{ 391 struct timer *t = uap; 392 393 t->func(t->sscop); 394 free(t); 395} 396 397/* 398 * Start a timer 399 */ 400void * 401sscop_start_timer(struct sscop *sscop, void *arg __unused, u_int msec, 402 void (*func)(void *)) 403{ 404 struct timer *t; 405 struct timespec due; 406 407 if ((t = malloc(sizeof(*t))) == NULL) 408 err(1, NULL); 409 t->sscop = sscop; 410 t->func = func; 411 412 due = evAddTime(evNowTime(), 413 evConsTime((time_t)msec/1000, (long)(msec%1000)*1000)); 414 415 if (evSetTimer(evctx, tfunc, t, due, evConsTime(0, 0), &t->id)) 416 err(1, "cannot start timer"); 417 418 return (t); 419} 420 421/* 422 * Stop a timer 423 */ 424void 425sscop_stop_timer(struct sscop *sscop __unused, void *arg __unused, void *tp) 426{ 427 struct timer *t = tp; 428 429 evClearTimer(evctx, t->id); 430 free(t); 431} 432