/* $Id: subr.c,v 10.1 92/10/06 23:10:44 ca Exp $ */ /* File for the simulator that contains random functions that don't belong anywhere else. */ #include #include #include #include #include #include "sim.h" #include "q.h" #include "list.h" #include "component.h" #include "log.h" #include "eventdefs.h" #include "peer.h" #ifdef DEBUG extern Log debug_log; #endif /********************************************* Routines for various types of parameter I/O. */ /* Simplest text routine */ /*ARGSUSED*/ char * make_text(c, p) Component *c; Param *p; { return(p->p_name); } /*ARGSUSED*/ char * make_str_text(c, p) Component *c; Param *p; { static char b[60]; sprintf(b, "%s: %s", p->p_name, (char *)(p->u.p)); return(b); } /*ARGSUSED*/ char * make_short_str_text(c, p) Component *c; Param *p; { static char buf[50]; sprintf(buf, "'%s'", (char *)(p->u.p)); return(buf); } /*ARGSUSED*/ char * make_name_text(c, p) Component *c; Param *p; { return(c->co_name); } /*ARGSUSED*/ char * make_short_name_text(c, p) Component *c; Param *p; { static char buf[50]; sprintf(buf, "'%s'", c->co_name); return(buf); } /* Input routine for the parameter that is the component's name. */ /*ARGSUSED*/ param_input_name(p, s, c) Param *p; char *s; Component *c; { strcpy(c->co_name, s); return(TRUE); } /* Return a string that consists of the name of the passed parameter, followed by a colon and its value. The pointer returned is to a static data area, and so the contents will change with each call. */ /*ARGSUSED*/ char * make_int_text(c, p) Component *c; Param *p; { static char b[60]; sprintf(b, "%s: %d", p->p_name, p->u.i); return(b); } /*ARGSUSED*/ char * make_short_int_text(c, p) Component *c; Param *p; { static char b[40]; sprintf(b, "%d", p->u.i); return(b); } /*ARGSUSED*/ double int_calc(c, p) Component *c; Param *p; { return(p->u.i / 10.0); } /* Same routines for doubles */ /*ARGSUSED*/ char * make_double_text(c, p) Component *c; Param *p; { static char b[100]; sprintf(b, "%s: %g", p->p_name, p->u.d); return(b); } /*ARGSUSED*/ char * make_short_double_text(c, p) Component *c; Param *p; { static char b[40]; sprintf(b, "%g", p->u.d); return(b); } /*ARGSUSED*/ double double_calc(c, p) Component *c; Param *p; { return(p->u.d / 10.0); } /* Return a string that consists of the name of the passed parameter followed by "TRUE" or "FALSE" depending on the value of the (integer) parameter. */ /*ARGSUSED*/ char * make_bool_text(c, p) Component *c; Param *p; { static char b[60]; sprintf(b, "%s : %s", p->p_name, p->u.i ? "TRUE" : "FALSE"); return(b); } /*ARGSUSED*/ char * make_short_bool_text(c, p) Component *c; Param *p; { return(p->u.i ? "TRUE" : "FALSE"); } /* The following three routines are especially written for the IO of the "Retransmission Strategy" parameter of the TCP connection.*/ /*ARGSUSED*/ char* make_char_text(c,p) Component *c; Param *p; { static char b[60]; switch (p->u.i){ case UNIX_4_2: sprintf(b,"%s: unix 4.2",p->p_name); break; case UNIX_4_3: sprintf(b,"%s: unix 4.3",p->p_name); break; case SLOW_START: sprintf(b,"%s: slow start",p->p_name); break; default: sprintf(b,"%s: unix 4.2",p->p_name); } return(b); } /*ARGSUSED*/ char* make_short_char_text(c,p) Component *c; Param *p; { static char b[40]; switch (p->u.i){ case UNIX_4_2: sprintf(b,"'unix 4.2'"); break; case UNIX_4_3: sprintf(b,"'unix 4.3'"); break; case SLOW_START: sprintf(b,"'slow start'"); break; default: sprintf(b,"'unix 4.2'"); } return(b); } /*ARGSUSED*/ param_input_char(p, s, c) Param *p; char *s; Component *c; { p->u.i = UNIX_4_2; if(!strcmp(s,"unix 4.2")) p->u.i = UNIX_4_2; if(!strcmp(s,"unix 4.3")) p->u.i = UNIX_4_3; if(!strcmp(s,"slow start")) p->u.i = SLOW_START; return(TRUE); } /*ARGSUSED*/ double bool_calc(c, p) Component *c; Param *p; { /* Very simple: Return 1.0 if true, 0.0 if not. Really should be something that changes colors rather than a meter, but we don't have anything besides meters yet. */ return(p->u.i ? 1.0 : 0.0); } /*ARGSUSED*/ double color_calc(c, p) Component *c; Param *p; { /* The value of the param is an integer number of a color. Just convert it to a double & return it. */ return((double)p->u.i); } /*ARGSUSED*/ double pktq_calc(c, p) Component *c; Param *p; { int qlen = ((queue *)p->u.p)->q_len; return((double)qlen / 10.0); } /*ARGSUSED*/ char * pktq_text(c, p) Component *c; Param *p; { int qlen = ((queue *)p->u.p)->q_len; static char b[80]; sprintf(b, "%s has %d pkts", p->p_name, qlen); return(b); } /*ARGSUSED*/ char * pktq_short_text(c, p) Component *c; Param *p; { int qlen = ((queue *)p->u.p)->q_len; static char b[20]; sprintf(b, "%d", qlen); return(b); } /* Generic queue routines (That don't say "packet" in their output). */ /*ARGSUSED*/ double q_calc(c, p) Component *c; Param *p; { int qlen = ((queue *)p->u.p)->q_len; /* Do the right thing--don't do any scaling here. */ return((double)qlen); } /*ARGSUSED*/ char * q_text(c, p) Component *c; Param *p; { int qlen = ((queue *)p->u.p)->q_len; static char b[80]; sprintf(b, "%s length %d", p->p_name, qlen); return(b); } /*ARGSUSED*/ char * q_short_text(c, p) Component *c; Param *p; { int qlen = ((queue *)p->u.p)->q_len; static char b[20]; sprintf(b, "%d", qlen); return(b); } /* Routine to do input for a param. Just converts string to int. */ /*ARGSUSED*/ param_input_int(p, s, c) Param *p; char *s; Component *c; { /* Minimal error check: make sure that first non-whitespace char is a digit. */ for (; isspace(*s); s++) ; if (isdigit(*s) || *s == '-') { p->u.i = atoi(s); return(TRUE); } return(FALSE); } /*ARGSUSED*/ param_input_str(p, s, c) Param *p; char *s; Component *c; { for (; isspace(*s); s++) ; strncpy(p->u.p, s, 40); return(TRUE); } /*ARGSUSED*/ param_input_double(p, s, c) Param *p; char *s; Component *c; { /* Minimal error check: make sure that first non-whitespace char is a digit. */ for (; isspace(*s); s++) ; if (isdigit(*s) || *s == '-' || *s == '.') { p->u.d = atof(s); return(TRUE); } return(FALSE); } /*ARGSUSED*/ param_input_bool(p, s, c) Param *p; char *s; Component *c; { char buf[256]; /* Skip whitespace */ for (; isspace(*s); s++) ; /* Copy the passed string into the buffer 'cause it's going to be changed. */ strcpy(buf, s); ucase(buf); if (strcmp("TRUE", buf) == 0 || atoi(buf) != 0 || strcmp("T", buf) == 0) p->u.i = TRUE; else p->u.i = FALSE; return(TRUE); } /******************************************************* Routines to replace calloc(), malloc(), and realloc(). They are guaranteed to either return a valid pointer or to not return. If the allocation fails, they cause a core dump. */ char * sim_malloc(size) register unsigned size; { register char *p = malloc(size); if (!p) panic("No memory\n"); return(p); } char * sim_calloc(nelem, elsize) register unsigned nelem, elsize; { register char *p = calloc(nelem, elsize); if (!p) panic("No memory\n"); return(p); } char * sim_realloc(ptr, size) register char *ptr; register unsigned size; { register char *p = realloc(ptr, size); if (!p) panic("No memory\n"); return(p); } /* Print a message and cause a core dump */ void panic(s) char *s; { fputs(s, stderr); fflush(stderr); abort(); } /************************************************** Miscellaneous useful functions. */ /* Routine to find a component in a list of neighbors. */ Neighbor * find_neighbor(l, c) register list *l; register Component *c; { register Neighbor *n; for (n = (Neighbor *)l->l_head; n; n = n->n_next) if (n->n_c == c) break; return(n); } /* Upper caseify a string */ ucase(s) register char *s; { while (*s) { if (islower(*s)) *s = toupper(*s); s++; } } /**** Routine to initialize a parameter. Pass it all the fields and a component, allocates a parameter, stuffs the fields into it, and adds it to the queue. The address of the new parameter is returned. */ Param * param_init(c, name, calc_val, make_text, make_short_text, input, display_type, flags, scale) Component *c; char *name; PFD calc_val; PFP make_text, make_short_text; PFI input; int display_type, flags; double scale; { register Param *p = (Param *)sim_calloc(1, sizeof(Param)); if (!p) return((Param *)NULL); strcpy(p->p_name, name); p->p_calc_val = calc_val; p->p_make_text = make_text; p->p_make_short_text = make_short_text; p->p_input = input; p->p_display_type = display_type; p->p_flags = flags; p->p_scale = scale; qe_addt(c->co_params, p); return(p); } /******************************************************* Functions to perform housekeeping that all components must do. */ /******** Things that all components do when they are deleted. */ comp_delete(c) Component *c; { Neighbor *n; /* UNEIGHBOR self from all neighbors */ for (n = (Neighbor *)c->co_neighbors->l_head; n; n = n->n_next) if (!((*(n->n_c->co_action))(c, n->n_c, EV_UNEIGHBOR, NULL, (caddr_t)c))) { #ifdef DEBUG dbg_write(debug_log, DBG_ERR, (Component *)c, "couldn't remove self from a neighbor while deleting"); #else ; #endif } /* Wipe out the neighbor list */ lq_delete(c->co_neighbors); /* Destroy parameter storage */ lq_delete(c->co_params); peer_or_delete(c, c); /* Wipe out this component */ free((char *)c); } /********* Add a neighbor to a component. Takes care of: Checking for max number of neighbors. Checking for duplicate neighbors. Only correct classes of neighbors. Any other types of error checking should be done *before* calling this, since this routine actually allocates memory and changes the component structure. This function returns a pointer to the new neighbor structure. The caller must initialize any parameters and packet queues in that neighbor structure. */ /*VARARGS4*/ Neighbor * add_neighbor(register Component *c, register Component *neighc, int max_num_neighbors, int num_classes, ... ) { va_list vp; register Neighbor *n; int notlegal; /* Check that have less than the max number of neighbors */ /* max_num_neighbors <= 0 means infinite number is permissible */ if (max_num_neighbors > 0) if (c->co_neighbors->l_len >= max_num_neighbors) { #ifdef DEBUG dbg_write(debug_log, DBG_ERR, c, "cannot attach more than %d neighbors", max_num_neighbors); #endif return((Neighbor *)NULL); } /* Check that it is not already a neighbor */ if (find_neighbor(c->co_neighbors, neighc)) { #ifdef DEBUG dbg_write(debug_log, DBG_ERR, c, "attempt to attach '%s' twice as neighbor", neighc->co_name); #endif return((Neighbor *)NULL); } if (num_classes > 0) { /* Final check--may sure the new neighbor is one of the legal classes */ va_start(vp, num_classes); notlegal = TRUE; while (num_classes--) if (neighc->co_class == va_arg(vp, int)) { notlegal = FALSE; break; } va_end(vp); } if (notlegal) { #ifdef DEBUG dbg_write(debug_log, DBG_ERR, c, "attempt to attach '%s'--not a legal neighbor", neighc->co_name); #endif return((Neighbor *)NULL); } /** Finally.... can really do it. */ n = (Neighbor *)sim_calloc(1, sizeof(Neighbor)); n->n_c = neighc; le_addt(c->co_neighbors, n); #ifdef DEBUG dbg_write(debug_log, DBG_INFO, c, "added '%s' as neighbor", neighc->co_name); #endif return(n); } /********** Generic routine to remove a neighbor from a component. Is passed a pointer to the neighbor component. */ remove_neighbor(c, neighc) register Component *c, *neighc; { register Neighbor *n; n = find_neighbor(c->co_neighbors, neighc); if (n) { le_del(c->co_neighbors, n); if (n->n_pq) lq_delete(n->n_pq); if (n->n_p) { qe_del(c->co_params, n->n_p); free(n->n_p); } if (n->n_pp) { qe_del(c->co_params, n->n_pp); free(n->n_pp); } if (n->n_data) { free (n->n_data); } free(n); #ifdef DEBUG dbg_write(debug_log, DBG_INFO, (Component *)c, "removed '%s' from neighbor list", neighc->co_name); #endif return(TRUE); } #ifdef DEBUG dbg_write(debug_log, DBG_ERR, (Component *)c, "tried to remove nonexistant neighbor '%s'", neighc->co_name); #endif return(FALSE); } /*** Return a random number r, s.t. a <= r <= b */ double random_range(a, b) register double a, b; { register double diff = b - a; return ((((double)random() / (double)MAX_RANDOM_NUMBER) * diff) + a); } #ifdef UNICOS /* Rint returns the integer (represented as a double precision number) nearest x in the direction of the prevailing round- ing mode. On a VAX, rint(x) is equivalent to adding half to the magni- tude and then rounding towards zero. In the default rounding mode, to nearest, on a machine that conforms to IEEE 754, rint(x) is the integer nearest x with the additional stipulation that if |rint(x)-x|=1/2 then rint(x) is even. Other rounding modes can make rint act like floor, or like ceil, or round towards zero. Another way to obtain an integer near x is to declare (in C) double x; int k; k = x; Most C compilers round x towards 0 to get the integer k, but some do otherwise. If in doubt, use floor, ceil, or rint first, whichever you intend. Also note that, if x is larger than k can accommodate, the value of k and the presence or absence of an integer overflow are hard to predict. */ #include double rint (x) double x; { double flr; flr = floor (x); return (((x - flr) >= (double) 0.5) ? ceil (x) : flr); } #endif /* UNICOS */ /* The following written at UMCP */ unsigned int random_no(type, average, deviation, lower, upper) int type; double average, deviation; unsigned int lower, upper; { unsigned int interval; double a, b; switch (type) { case 0: /* Exponential */ interval = (unsigned int) (-average * log((double) random () / (double) MAX_RANDOM_NUMBER)); break; case 1: /* Uniform */ a = average - 1.732 * deviation; b = average + 1.732 * deviation; interval = (unsigned int) (a + (b - a) * (double) random () / (double) MAX_RANDOM_NUMBER); break; case 2: /* Geometric */ interval = log((double) random () / (double) MAX_RANDOM_NUMBER) / log((double)((double)1.0 - 1.0 / average)); break; } return min(max(interval, lower), upper); }