Ignore:
Timestamp:
Nov 6, 2007, 11:01:53 AM (13 years ago)
Author:
Bruno Cornec
Message:
  • Better output for mindi-busybox revision
  • Remove dummy file created on NFS - report from Arnaud Tiger <arnaud.tiger_at_hp.com>
  • strace useful for debug
  • fix new versions for pb (2.0.0 for mindi and 1.7.2 for mindi-busybox)
  • fix build process for mindi-busybox + options used in that version (dd for label-partitions-as-necessary)
  • fix typo in label-partitions-as-necessary which doesn't seem to work
  • Update to busybox 1.7.2
  • perl is now required at restore time to support uuid swap partitions (and will be used for many other thigs

in the future for sure)

  • next mindi version will be 2.0.0 due to all the changes made in it (udev may break working distros)
  • small optimization in mindi on keyboard handling (one single find instead of multiple)
  • better interaction for USB device when launching mindi manually
  • attempt to automatically guess block disk size for ramdisk
  • fix typos in bkphw
  • Fix the remaining problem with UUID support for swap partitions
  • Updates mondoarchive man page for USB support
  • Adds preliminary Hardware support to mindi (Proliant SSSTK)
  • Tries to add udev support also for rhel4
  • Fix UUID support which was still broken.
  • Be conservative in test for the start-nfs script
  • Update config file for mindi-busybox for 1.7.2 migration
  • Try to run around a busybox bug (1.2.2 pb on inexistant links)
  • Add build content for mindi-busybox in pb
  • Remove distributions content for mindi-busybox
  • Fix a warning on inexistant raidtab
  • Solve problem on tmpfs in restore init (Problem of inexistant symlink and busybox)
  • Create MONDO_CACHE and use it everywhere + creation at start
  • Really never try to eject a USB device
  • Fix a issue with &> usage (replaced with 1> and 2>)
  • Adds magic file to depllist in order to have file working + ldd which helps for debugging issues
  • tty modes correct to avoid sh error messages
  • Use ext3 normally and not ext2 instead
  • USB device should be corrected after reading (take 1st part)
  • Adds a mount_USB_here function derived from mount_CDROM_here
  • usb detection place before /dev detection in device name at restore time
  • Fix when restoring from USB: media is asked in interactive mode
  • Adds USB support for mondorestore
  • mount_cdrom => mount_media
  • elilo.efi is now searched throughout /boot/efi and not in a fixed place as there is no standard
  • untar-and-softlink => untar (+ interface change)
  • suppress useless softlinks creation/removal in boot process
  • avoids udevd messages on groups
  • Increase # of disks to 99 as in mindi at restore time (should be a conf file parameter)
  • skip existing big file creation
  • seems to work correctly for USB mindi boot
  • Adds group and tty link to udev conf
  • Always load usb-torage (even 2.6) to initiate USB bus discovery
  • Better printing of messages
  • Attempt to fix a bug in supporting OpenSusE 10.3 kernel for initramfs (mindi may now use multiple regex for kernel initrd detection)
  • Links were not correctly done as non relative for modules in mindi
  • exclusion of modules denied now works
  • Also create modules in their ordinary place, so that classical modprobe works + copy modules.dep
  • Fix bugs for DENY_MODS handling
  • Add device /dev/console for udev
  • ide-generic should now really be excluded
  • Fix a bug in major number for tty
  • If udev then adds modprobe/insmod to rootfs
  • tty0 is also cretaed with udev
  • ide-generic put rather in DENY_MODS
  • udevd remove from deplist s handled in mindi directly
  • better default for mindi when using --usb
  • Handles dynamically linked busybox (in case we want to use it soon ;-)
  • Adds fixed devices to create for udev
  • ide-generic should not be part of the initrd when using libata v2
  • support a dynamically linked udev (case on Ubuntu 7.10 and Mandriva 2008.0 so should be quite generic) This will give incitation to move to dyn. linked binaries in the initrd which will help for other tasks (ia6 4)
  • Improvement in udev support (do not use cl options not available in busybox)
  • Udev in mindi
    • auto creation of the right links at boot time with udev-links.conf(from Mandriva 2008.0)
    • rework startup of udev as current makes kernel crash (from Mandriva 2008.0)
    • add support for 64 bits udev
  • Try to render MyInsmod? silent at boot time
  • Adds udev support (mandatory for newest distributions to avoid remapping of devices in a different way as on the original system)
  • We also need vaft format support for USB boot
  • Adds libusual support (Ubuntu 7.10 needs it for USB)
  • Improve Ubuntu/Debian? keyboard detection and support
  • pbinit adapted to new pb (0.8.10). Filtering of docs done in it
  • Suppress some mondo warnings and errors on USB again
  • Tries to fix lack of files in deb mindi package
  • Verify should now work for USB devices
  • More log/mesages improvement for USB support
  • - Supress g_erase_tmpdir_and_scratchdir
  • Improve some log messages for USB support
  • Try to improve install in mindi to avoid issues with isolinux.cfg not installed vene if in the pkg :-(
  • Improve mindi-busybox build
  • In conformity with pb 0.8.9
  • Add support for Ubuntu 7.10 in build process
  • Add USB Key button to Menu UI (CD streamer removed)
  • Attempt to fix error messages on tmp/scratch files at the end by removing those dir at the latest possible.
  • Fix a bug linked to the size of the -E param which could be used (Arnaud Tiger/René? Ribaud).
  • Integrate ~/.pbrc content into mondorescue.pb (required project-builder >= 0.8.7)
  • Put mondorescue in conformity with new pb filtering rules
  • Add USB support at restore time (no test done yet). New start-usb script PB varibale added where useful
  • Unmounting USB device before removal of temporary scratchdir
  • Stil refining USB copy back to mondo (one command was not executed)
  • No need to have the image subdor in the csratchdir when USB.
  • umount the USB partition before attempting to use it
  • Remove useless copy from mindi to mondo at end of USB handling

(risky merge, we are raising the limits of 2 diverging branches. The status of stable is not completely sure as such. Will need lots of tests, but it's not yet done :-()
(merge -r1692:1769 $SVN_M/branches/2.2.5)

File:
1 edited

Legend:

Unmodified
Added
Removed
  • branches/stable/mindi-busybox/networking/ping.c

    r821 r1770  
    11/* vi: set sw=4 ts=4: */
    22/*
    3  * $Id: ping.c,v 1.56 2004/03/15 08:28:48 andersen Exp $
    43 * Mini ping implementation for busybox
    54 *
     
    87 * Adapted from the ping in netkit-base 0.10:
    98 * Copyright (c) 1989 The Regents of the University of California.
    10  * Derived from software contributed to Berkeley by Mike Muuss.
     9 * All rights reserved.
     10 *
     11 * This code is derived from software contributed to Berkeley by
     12 * Mike Muuss.
    1113 *
    1214 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
    1315 */
    14 
    15 #include <sys/param.h>
    16 #include <sys/socket.h>
    17 #include <sys/file.h>
    18 #include <sys/times.h>
    19 #include <signal.h>
    20 
    21 #include <netinet/in.h>
    22 #include <netinet/ip.h>
     16/* from ping6.c:
     17 * Copyright (C) 1999 by Randolph Chung <tausq@debian.org>
     18 *
     19 * This version of ping is adapted from the ping in netkit-base 0.10,
     20 * which is:
     21 *
     22 * Original copyright notice is retained at the end of this file.
     23 *
     24 * This version is an adaptation of ping.c from busybox.
     25 * The code was modified by Bart Visscher <magick@linux-fan.com>
     26 */
     27
     28#include <net/if.h>
    2329#include <netinet/ip_icmp.h>
    24 #include <arpa/inet.h>
    25 #include <netdb.h>
    26 #include <stdio.h>
    27 #include <stdlib.h>
    28 #include <errno.h>
    29 #include <unistd.h>
    30 #include <string.h>
    31 #include <stdlib.h>
    32 #include "busybox.h"
    33 
     30#include "libbb.h"
     31
     32#if ENABLE_PING6
     33#include <netinet/icmp6.h>
     34/* I see RENUMBERED constants in bits/in.h - !!?
     35 * What a fuck is going on with libc? Is it a glibc joke? */
     36#ifdef IPV6_2292HOPLIMIT
     37#undef IPV6_HOPLIMIT
     38#define IPV6_HOPLIMIT IPV6_2292HOPLIMIT
     39#endif
     40#endif
    3441
    3542enum {
     
    4047    MAX_DUP_CHK = (8 * 128),
    4148    MAXWAIT = 10,
    42     PINGINTERVAL = 1        /* second */
     49    PINGINTERVAL = 1, /* 1 second */
    4350};
    4451
    45 #define O_QUIET         (1 << 0)
     52/* common routines */
     53
     54static int in_cksum(unsigned short *buf, int sz)
     55{
     56    int nleft = sz;
     57    int sum = 0;
     58    unsigned short *w = buf;
     59    unsigned short ans = 0;
     60
     61    while (nleft > 1) {
     62        sum += *w++;
     63        nleft -= 2;
     64    }
     65
     66    if (nleft == 1) {
     67        *(unsigned char *) (&ans) = *(unsigned char *) w;
     68        sum += ans;
     69    }
     70
     71    sum = (sum >> 16) + (sum & 0xFFFF);
     72    sum += (sum >> 16);
     73    ans = ~sum;
     74    return ans;
     75}
     76
     77#if !ENABLE_FEATURE_FANCY_PING
     78
     79/* simple version */
     80
     81static char *hostname;
     82
     83static void noresp(int ign ATTRIBUTE_UNUSED)
     84{
     85    printf("No response from %s\n", hostname);
     86    exit(EXIT_FAILURE);
     87}
     88
     89static void ping4(len_and_sockaddr *lsa)
     90{
     91    struct sockaddr_in pingaddr;
     92    struct icmp *pkt;
     93    int pingsock, c;
     94    char packet[DEFDATALEN + MAXIPLEN + MAXICMPLEN];
     95
     96    pingsock = create_icmp_socket();
     97    pingaddr = lsa->sin;
     98
     99    pkt = (struct icmp *) packet;
     100    memset(pkt, 0, sizeof(packet));
     101    pkt->icmp_type = ICMP_ECHO;
     102    pkt->icmp_cksum = in_cksum((unsigned short *) pkt, sizeof(packet));
     103
     104    c = xsendto(pingsock, packet, DEFDATALEN + ICMP_MINLEN,
     105               (struct sockaddr *) &pingaddr, sizeof(pingaddr));
     106
     107    /* listen for replies */
     108    while (1) {
     109        struct sockaddr_in from;
     110        socklen_t fromlen = sizeof(from);
     111
     112        c = recvfrom(pingsock, packet, sizeof(packet), 0,
     113                (struct sockaddr *) &from, &fromlen);
     114        if (c < 0) {
     115            if (errno != EINTR)
     116                bb_perror_msg("recvfrom");
     117            continue;
     118        }
     119        if (c >= 76) {          /* ip + icmp */
     120            struct iphdr *iphdr = (struct iphdr *) packet;
     121
     122            pkt = (struct icmp *) (packet + (iphdr->ihl << 2)); /* skip ip hdr */
     123            if (pkt->icmp_type == ICMP_ECHOREPLY)
     124                break;
     125        }
     126    }
     127    if (ENABLE_FEATURE_CLEAN_UP)
     128        close(pingsock);
     129}
     130
     131#if ENABLE_PING6
     132static void ping6(len_and_sockaddr *lsa)
     133{
     134    struct sockaddr_in6 pingaddr;
     135    struct icmp6_hdr *pkt;
     136    int pingsock, c;
     137    int sockopt;
     138    char packet[DEFDATALEN + MAXIPLEN + MAXICMPLEN];
     139
     140    pingsock = create_icmp6_socket();
     141    pingaddr = lsa->sin6;
     142
     143    pkt = (struct icmp6_hdr *) packet;
     144    memset(pkt, 0, sizeof(packet));
     145    pkt->icmp6_type = ICMP6_ECHO_REQUEST;
     146
     147    sockopt = offsetof(struct icmp6_hdr, icmp6_cksum);
     148    setsockopt(pingsock, SOL_RAW, IPV6_CHECKSUM, &sockopt, sizeof(sockopt));
     149
     150    c = xsendto(pingsock, packet, DEFDATALEN + sizeof (struct icmp6_hdr),
     151               (struct sockaddr *) &pingaddr, sizeof(pingaddr));
     152
     153    /* listen for replies */
     154    while (1) {
     155        struct sockaddr_in6 from;
     156        socklen_t fromlen = sizeof(from);
     157
     158        c = recvfrom(pingsock, packet, sizeof(packet), 0,
     159                (struct sockaddr *) &from, &fromlen);
     160        if (c < 0) {
     161            if (errno != EINTR)
     162                bb_perror_msg("recvfrom");
     163            continue;
     164        }
     165        if (c >= 8) {           /* icmp6_hdr */
     166            pkt = (struct icmp6_hdr *) packet;
     167            if (pkt->icmp6_type == ICMP6_ECHO_REPLY)
     168                break;
     169        }
     170    }
     171    if (ENABLE_FEATURE_CLEAN_UP)
     172        close(pingsock);
     173}
     174#endif
     175
     176int ping_main(int argc, char **argv);
     177int ping_main(int argc, char **argv)
     178{
     179    len_and_sockaddr *lsa;
     180#if ENABLE_PING6
     181    sa_family_t af = AF_UNSPEC;
     182
     183    while ((++argv)[0] && argv[0][0] == '-') {
     184        if (argv[0][1] == '4') {
     185            af = AF_INET;
     186            continue;
     187        }
     188        if (argv[0][1] == '6') {
     189            af = AF_INET6;
     190            continue;
     191        }
     192        bb_show_usage();
     193    }
     194#else
     195    argv++;
     196#endif
     197
     198    hostname = *argv;
     199    if (!hostname)
     200        bb_show_usage();
     201
     202#if ENABLE_PING6
     203    lsa = xhost_and_af2sockaddr(hostname, 0, af);
     204#else
     205    lsa = xhost_and_af2sockaddr(hostname, 0, AF_INET);
     206#endif
     207    /* Set timer _after_ DNS resolution */
     208    signal(SIGALRM, noresp);
     209    alarm(5); /* give the host 5000ms to respond */
     210
     211#if ENABLE_PING6
     212    if (lsa->sa.sa_family == AF_INET6)
     213        ping6(lsa);
     214    else
     215#endif
     216        ping4(lsa);
     217    printf("%s is alive!\n", hostname);
     218    return EXIT_SUCCESS;
     219}
     220
     221
     222#else /* FEATURE_FANCY_PING */
     223
     224
     225/* full(er) version */
     226
     227#define OPT_STRING ("qvc:s:I:4" USE_PING6("6"))
     228enum {
     229    OPT_QUIET = 1 << 0,
     230    OPT_VERBOSE = 1 << 1,
     231    OPT_c = 1 << 2,
     232    OPT_s = 1 << 3,
     233    OPT_I = 1 << 4,
     234    OPT_IPV4 = 1 << 5,
     235    OPT_IPV6 = (1 << 6) * ENABLE_PING6,
     236};
     237
     238
     239struct globals {
     240    int pingsock;
     241    len_and_sockaddr *source_lsa;
     242    unsigned datalen;
     243    int if_index;
     244    unsigned long ntransmitted, nreceived, nrepeats, pingcount;
     245    uint16_t myid;
     246    unsigned tmin, tmax; /* in us */
     247    unsigned long long tsum; /* in us, sum of all times */
     248    const char *hostname;
     249    const char *dotted;
     250    union {
     251        struct sockaddr sa;
     252        struct sockaddr_in sin;
     253#if ENABLE_PING6
     254        struct sockaddr_in6 sin6;
     255#endif
     256    } pingaddr;
     257    char rcvd_tbl[MAX_DUP_CHK / 8];
     258};
     259#define G (*(struct globals*)&bb_common_bufsiz1)
     260#define pingsock     (G.pingsock    )
     261#define source_lsa   (G.source_lsa  )
     262#define datalen      (G.datalen     )
     263#define if_index     (G.if_index    )
     264#define ntransmitted (G.ntransmitted)
     265#define nreceived    (G.nreceived   )
     266#define nrepeats     (G.nrepeats    )
     267#define pingcount    (G.pingcount   )
     268#define myid         (G.myid        )
     269#define tmin         (G.tmin        )
     270#define tmax         (G.tmax        )
     271#define tsum         (G.tsum        )
     272#define hostname     (G.hostname    )
     273#define dotted       (G.dotted      )
     274#define pingaddr     (G.pingaddr    )
     275#define rcvd_tbl     (G.rcvd_tbl    )
     276void BUG_ping_globals_too_big(void);
     277#define INIT_G() do { \
     278        if (sizeof(G) > COMMON_BUFSIZE) \
     279                BUG_ping_globals_too_big(); \
     280    pingsock = -1; \
     281    tmin = UINT_MAX; \
     282} while (0)
     283
    46284
    47285#define A(bit)      rcvd_tbl[(bit)>>3]  /* identify byte in array */
     
    51289#define TST(bit)    (A(bit) & B(bit))
    52290
    53 static void ping(const char *host);
    54 
    55 /* common routines */
    56 static int in_cksum(unsigned short *buf, int sz)
    57 {
    58     int nleft = sz;
    59     int sum = 0;
    60     unsigned short *w = buf;
    61     unsigned short ans = 0;
    62 
    63     while (nleft > 1) {
    64         sum += *w++;
    65         nleft -= 2;
    66     }
    67 
    68     if (nleft == 1) {
    69         *(unsigned char *) (&ans) = *(unsigned char *) w;
    70         sum += ans;
    71     }
    72 
    73     sum = (sum >> 16) + (sum & 0xFFFF);
    74     sum += (sum >> 16);
    75     ans = ~sum;
    76     return (ans);
    77 }
    78 
    79 /* simple version */
    80 #ifndef CONFIG_FEATURE_FANCY_PING
    81 static char *hostname = NULL;
    82 static void noresp(int ign)
    83 {
    84     printf("No response from %s\n", hostname);
    85     exit(EXIT_FAILURE);
    86 }
    87 
    88 static void ping(const char *host)
    89 {
    90     struct hostent *h;
    91     struct sockaddr_in pingaddr;
    92     struct icmp *pkt;
    93     int pingsock, c;
    94     char packet[DEFDATALEN + MAXIPLEN + MAXICMPLEN];
    95 
    96     pingsock = create_icmp_socket();
    97 
    98     memset(&pingaddr, 0, sizeof(struct sockaddr_in));
    99 
    100     pingaddr.sin_family = AF_INET;
    101     h = xgethostbyname(host);
    102     memcpy(&pingaddr.sin_addr, h->h_addr, sizeof(pingaddr.sin_addr));
    103     hostname = h->h_name;
    104 
    105     pkt = (struct icmp *) packet;
    106     memset(pkt, 0, sizeof(packet));
    107     pkt->icmp_type = ICMP_ECHO;
    108     pkt->icmp_cksum = in_cksum((unsigned short *) pkt, sizeof(packet));
    109 
    110     c = sendto(pingsock, packet, DEFDATALEN + ICMP_MINLEN, 0,
    111                (struct sockaddr *) &pingaddr, sizeof(struct sockaddr_in));
    112 
    113     if (c < 0) {
    114         if (ENABLE_FEATURE_CLEAN_UP) close(pingsock);
    115         bb_perror_msg_and_die("sendto");
    116     }
    117 
    118     signal(SIGALRM, noresp);
    119     alarm(5);                   /* give the host 5000ms to respond */
    120     /* listen for replies */
    121     while (1) {
    122         struct sockaddr_in from;
    123         socklen_t fromlen = sizeof(from);
    124 
    125         if ((c = recvfrom(pingsock, packet, sizeof(packet), 0,
    126                           (struct sockaddr *) &from, &fromlen)) < 0) {
    127             if (errno == EINTR)
    128                 continue;
    129             bb_perror_msg("recvfrom");
    130             continue;
    131         }
    132         if (c >= 76) {          /* ip + icmp */
    133             struct iphdr *iphdr = (struct iphdr *) packet;
    134 
    135             pkt = (struct icmp *) (packet + (iphdr->ihl << 2)); /* skip ip hdr */
    136             if (pkt->icmp_type == ICMP_ECHOREPLY)
    137                 break;
    138         }
    139     }
    140     if (ENABLE_FEATURE_CLEAN_UP) close(pingsock);
    141     printf("%s is alive!\n", hostname);
    142     return;
    143 }
    144 
    145 int ping_main(int argc, char **argv)
    146 {
    147     argc--;
    148     argv++;
    149     if (argc < 1)
    150         bb_show_usage();
    151     ping(*argv);
    152     return EXIT_SUCCESS;
    153 }
    154 
    155 #else /* ! CONFIG_FEATURE_FANCY_PING */
    156 /* full(er) version */
    157 static struct sockaddr_in pingaddr;
    158 static int pingsock = -1;
    159 static int datalen; /* intentionally uninitialized to work around gcc bug */
    160 
    161 static long ntransmitted, nreceived, nrepeats, pingcount;
    162 static int myid, options;
    163 static unsigned long tmin = ULONG_MAX, tmax, tsum;
    164 static char rcvd_tbl[MAX_DUP_CHK / 8];
    165 
    166 #ifndef CONFIG_FEATURE_FANCY_PING6
    167 static
    168 #endif
    169     struct hostent *hostent;
    170 
    171 static void sendping(int);
    172 static void pingstats(int);
    173 static void unpack(char *, int, struct sockaddr_in *);
    174 
    175291/**************************************************************************/
    176292
    177 static void pingstats(int junk)
    178 {
    179     int status;
    180 
     293static void pingstats(int junk ATTRIBUTE_UNUSED)
     294{
    181295    signal(SIGINT, SIG_IGN);
    182296
    183     printf("\n--- %s ping statistics ---\n", hostent->h_name);
    184     printf("%ld packets transmitted, ", ntransmitted);
    185     printf("%ld packets received, ", nreceived);
     297    printf("\n--- %s ping statistics ---\n", hostname);
     298    printf("%lu packets transmitted, ", ntransmitted);
     299    printf("%lu packets received, ", nreceived);
    186300    if (nrepeats)
    187         printf("%ld duplicates, ", nrepeats);
     301        printf("%lu duplicates, ", nrepeats);
    188302    if (ntransmitted)
    189         printf("%ld%% packet loss\n",
    190                (ntransmitted - nreceived) * 100 / ntransmitted);
    191     if (nreceived)
    192         printf("round-trip min/avg/max = %lu.%lu/%lu.%lu/%lu.%lu ms\n",
    193                tmin / 10, tmin % 10,
    194                (tsum / (nreceived + nrepeats)) / 10,
    195                (tsum / (nreceived + nrepeats)) % 10, tmax / 10, tmax % 10);
    196     if (nreceived != 0)
    197         status = EXIT_SUCCESS;
    198     else
    199         status = EXIT_FAILURE;
    200     exit(status);
    201 }
    202 
    203 static void sendping(int junk)
    204 {
    205     struct icmp *pkt;
    206     int i;
    207     char packet[datalen + ICMP_MINLEN];
    208 
    209     pkt = (struct icmp *) packet;
     303        ntransmitted = (ntransmitted - nreceived) * 100 / ntransmitted;
     304    printf("%lu%% packet loss\n", ntransmitted);
     305    if (tmin != UINT_MAX) {
     306        unsigned tavg = tsum / (nreceived + nrepeats);
     307        printf("round-trip min/avg/max = %u.%03u/%u.%03u/%u.%03u ms\n",
     308            tmin / 1000, tmin % 1000,
     309            tavg / 1000, tavg % 1000,
     310            tmax / 1000, tmax % 1000);
     311    }
     312    exit(nreceived == 0); /* (nreceived == 0) is true (1) -- 'failure' */
     313}
     314
     315static void sendping_tail(void (*sp)(int), const void *pkt, int size_pkt)
     316{
     317    int sz;
     318
     319    CLR((uint16_t)ntransmitted % MAX_DUP_CHK);
     320    ntransmitted++;
     321
     322    /* sizeof(pingaddr) can be larger than real sa size, but I think
     323     * it doesn't matter */
     324    sz = xsendto(pingsock, pkt, size_pkt, &pingaddr.sa, sizeof(pingaddr));
     325    if (sz != size_pkt)
     326        bb_error_msg_and_die(bb_msg_write_error);
     327
     328    signal(SIGALRM, sp);
     329    if (pingcount == 0 || ntransmitted < pingcount) { /* schedule next in 1s */
     330        alarm(PINGINTERVAL);
     331    } else { /* done, wait for the last ping to come back */
     332        /* todo, don't necessarily need to wait so long... */
     333        signal(SIGALRM, pingstats);
     334        alarm(MAXWAIT);
     335    }
     336}
     337
     338static void sendping4(int junk ATTRIBUTE_UNUSED)
     339{
     340    /* +4 reserves a place for timestamp, which may end up sitting
     341     * *after* packet. Saves one if() */
     342    struct icmp *pkt = alloca(datalen + ICMP_MINLEN + 4);
    210343
    211344    pkt->icmp_type = ICMP_ECHO;
    212345    pkt->icmp_code = 0;
    213346    pkt->icmp_cksum = 0;
    214     pkt->icmp_seq = htons(ntransmitted++);
     347    pkt->icmp_seq = htons(ntransmitted); /* don't ++ here, it can be a macro */
    215348    pkt->icmp_id = myid;
    216     CLR(ntohs(pkt->icmp_seq) % MAX_DUP_CHK);
    217 
    218     gettimeofday((struct timeval *) &pkt->icmp_dun, NULL);
    219     pkt->icmp_cksum = in_cksum((unsigned short *) pkt, sizeof(packet));
    220 
    221     i = sendto(pingsock, packet, sizeof(packet), 0,
    222                (struct sockaddr *) &pingaddr, sizeof(struct sockaddr_in));
    223 
    224     if (i < 0)
    225         bb_perror_msg_and_die("sendto");
    226     else if ((size_t)i != sizeof(packet))
    227         bb_error_msg_and_die("ping wrote %d chars; %d expected", i,
    228                (int)sizeof(packet));
    229 
    230     signal(SIGALRM, sendping);
    231     if (pingcount == 0 || ntransmitted < pingcount) {   /* schedule next in 1s */
    232         alarm(PINGINTERVAL);
    233     } else {                    /* done, wait for the last ping to come back */
    234         /* todo, don't necessarily need to wait so long... */
    235         signal(SIGALRM, pingstats);
    236         alarm(MAXWAIT);
    237     }
    238 }
    239 
    240 static char *icmp_type_name (int id)
     349
     350    /* We don't do hton, because we will read it back on the same machine */
     351    /*if (datalen >= 4)*/
     352        *(uint32_t*)&pkt->icmp_dun = monotonic_us();
     353
     354    pkt->icmp_cksum = in_cksum((unsigned short *) pkt, datalen + ICMP_MINLEN);
     355
     356    sendping_tail(sendping4, pkt, datalen + ICMP_MINLEN);
     357}
     358#if ENABLE_PING6
     359static void sendping6(int junk ATTRIBUTE_UNUSED)
     360{
     361    struct icmp6_hdr *pkt = alloca(datalen + sizeof(struct icmp6_hdr) + 4);
     362
     363    pkt->icmp6_type = ICMP6_ECHO_REQUEST;
     364    pkt->icmp6_code = 0;
     365    pkt->icmp6_cksum = 0;
     366    pkt->icmp6_seq = htons(ntransmitted); /* don't ++ here, it can be a macro */
     367    pkt->icmp6_id = myid;
     368
     369    /*if (datalen >= 4)*/
     370        *(uint32_t*)(&pkt->icmp6_data8[4]) = monotonic_us();
     371
     372    sendping_tail(sendping6, pkt, datalen + sizeof(struct icmp6_hdr));
     373}
     374#endif
     375
     376static const char *icmp_type_name(int id)
    241377{
    242378    switch (id) {
    243     case ICMP_ECHOREPLY:        return "Echo Reply";
    244     case ICMP_DEST_UNREACH:     return "Destination Unreachable";
    245     case ICMP_SOURCE_QUENCH:    return "Source Quench";
    246     case ICMP_REDIRECT:         return "Redirect (change route)";
    247     case ICMP_ECHO:             return "Echo Request";
    248     case ICMP_TIME_EXCEEDED:    return "Time Exceeded";
    249     case ICMP_PARAMETERPROB:    return "Parameter Problem";
    250     case ICMP_TIMESTAMP:        return "Timestamp Request";
    251     case ICMP_TIMESTAMPREPLY:   return "Timestamp Reply";
    252     case ICMP_INFO_REQUEST:     return "Information Request";
    253     case ICMP_INFO_REPLY:       return "Information Reply";
    254     case ICMP_ADDRESS:          return "Address Mask Request";
    255     case ICMP_ADDRESSREPLY:     return "Address Mask Reply";
    256     default:                    return "unknown ICMP type";
    257     }
    258 }
    259 
    260 static void unpack(char *buf, int sz, struct sockaddr_in *from)
    261 {
    262     struct icmp *icmppkt;
    263     struct iphdr *iphdr;
    264     struct timeval tv, *tp;
    265     int hlen, dupflag;
    266     unsigned long triptime;
    267 
    268     gettimeofday(&tv, NULL);
    269 
    270     /* check IP header */
    271     iphdr = (struct iphdr *) buf;
    272     hlen = iphdr->ihl << 2;
    273     /* discard if too short */
    274     if (sz < (datalen + ICMP_MINLEN))
    275         return;
    276 
    277     sz -= hlen;
    278     icmppkt = (struct icmp *) (buf + hlen);
    279 
    280     if (icmppkt->icmp_id != myid)
    281         return;             /* not our ping */
    282 
    283     if (icmppkt->icmp_type == ICMP_ECHOREPLY) {
    284         u_int16_t recv_seq = ntohs(icmppkt->icmp_seq);
    285         ++nreceived;
    286         tp = (struct timeval *) icmppkt->icmp_data;
    287 
    288         if ((tv.tv_usec -= tp->tv_usec) < 0) {
    289             --tv.tv_sec;
    290             tv.tv_usec += 1000000;
    291         }
    292         tv.tv_sec -= tp->tv_sec;
    293 
    294         triptime = tv.tv_sec * 10000 + (tv.tv_usec / 100);
     379    case ICMP_ECHOREPLY:      return "Echo Reply";
     380    case ICMP_DEST_UNREACH:   return "Destination Unreachable";
     381    case ICMP_SOURCE_QUENCH:  return "Source Quench";
     382    case ICMP_REDIRECT:       return "Redirect (change route)";
     383    case ICMP_ECHO:           return "Echo Request";
     384    case ICMP_TIME_EXCEEDED:  return "Time Exceeded";
     385    case ICMP_PARAMETERPROB:  return "Parameter Problem";
     386    case ICMP_TIMESTAMP:      return "Timestamp Request";
     387    case ICMP_TIMESTAMPREPLY: return "Timestamp Reply";
     388    case ICMP_INFO_REQUEST:   return "Information Request";
     389    case ICMP_INFO_REPLY:     return "Information Reply";
     390    case ICMP_ADDRESS:        return "Address Mask Request";
     391    case ICMP_ADDRESSREPLY:   return "Address Mask Reply";
     392    default:                  return "unknown ICMP type";
     393    }
     394}
     395#if ENABLE_PING6
     396/* RFC3542 changed some definitions from RFC2292 for no good reason, whee!
     397 * the newer 3542 uses a MLD_ prefix where as 2292 uses ICMP6_ prefix */
     398#ifndef MLD_LISTENER_QUERY
     399# define MLD_LISTENER_QUERY ICMP6_MEMBERSHIP_QUERY
     400#endif
     401#ifndef MLD_LISTENER_REPORT
     402# define MLD_LISTENER_REPORT ICMP6_MEMBERSHIP_REPORT
     403#endif
     404#ifndef MLD_LISTENER_REDUCTION
     405# define MLD_LISTENER_REDUCTION ICMP6_MEMBERSHIP_REDUCTION
     406#endif
     407static const char *icmp6_type_name(int id)
     408{
     409    switch (id) {
     410    case ICMP6_DST_UNREACH:      return "Destination Unreachable";
     411    case ICMP6_PACKET_TOO_BIG:   return "Packet too big";
     412    case ICMP6_TIME_EXCEEDED:    return "Time Exceeded";
     413    case ICMP6_PARAM_PROB:       return "Parameter Problem";
     414    case ICMP6_ECHO_REPLY:       return "Echo Reply";
     415    case ICMP6_ECHO_REQUEST:     return "Echo Request";
     416    case MLD_LISTENER_QUERY:     return "Listener Query";
     417    case MLD_LISTENER_REPORT:    return "Listener Report";
     418    case MLD_LISTENER_REDUCTION: return "Listener Reduction";
     419    default:                     return "unknown ICMP type";
     420    }
     421}
     422#endif
     423
     424static void unpack_tail(int sz, uint32_t *tp,
     425        const char *from_str,
     426        uint16_t recv_seq, int ttl)
     427{
     428    const char *dupmsg = " (DUP!)";
     429    unsigned triptime = triptime; /* for gcc */
     430
     431    ++nreceived;
     432
     433    if (tp) {
     434        /* (int32_t) cast is for hypothetical 64-bit unsigned */
     435        /* (doesn't hurt 32-bit real-world anyway) */
     436        triptime = (int32_t) ((uint32_t)monotonic_us() - *tp);
    295437        tsum += triptime;
    296438        if (triptime < tmin)
     
    298440        if (triptime > tmax)
    299441            tmax = triptime;
    300 
    301         if (TST(recv_seq % MAX_DUP_CHK)) {
    302             ++nrepeats;
    303             --nreceived;
    304             dupflag = 1;
    305         } else {
    306             SET(recv_seq % MAX_DUP_CHK);
    307             dupflag = 0;
    308         }
    309 
    310         if (options & O_QUIET)
    311             return;
    312 
    313         printf("%d bytes from %s: icmp_seq=%u", sz,
    314                inet_ntoa(*(struct in_addr *) &from->sin_addr.s_addr),
    315                recv_seq);
    316         printf(" ttl=%d", iphdr->ttl);
    317         printf(" time=%lu.%lu ms", triptime / 10, triptime % 10);
    318         if (dupflag)
    319             printf(" (DUP!)");
    320         printf("\n");
    321     } else
    322         if (icmppkt->icmp_type != ICMP_ECHO)
    323             bb_error_msg("Warning: Got ICMP %d (%s)",
    324                     icmppkt->icmp_type, icmp_type_name (icmppkt->icmp_type));
     442    }
     443
     444    if (TST(recv_seq % MAX_DUP_CHK)) {
     445        ++nrepeats;
     446        --nreceived;
     447    } else {
     448        SET(recv_seq % MAX_DUP_CHK);
     449        dupmsg += 7;
     450    }
     451
     452    if (option_mask32 & OPT_QUIET)
     453        return;
     454
     455    printf("%d bytes from %s: seq=%u ttl=%d", sz,
     456        from_str, recv_seq, ttl);
     457    if (tp)
     458        printf(" time=%u.%03u ms", triptime / 1000, triptime % 1000);
     459    puts(dupmsg);
    325460    fflush(stdout);
    326461}
    327 
    328 static void ping(const char *host)
     462static void unpack4(char *buf, int sz, struct sockaddr_in *from)
     463{
     464    struct icmp *icmppkt;
     465    struct iphdr *iphdr;
     466    int hlen;
     467
     468    /* discard if too short */
     469    if (sz < (datalen + ICMP_MINLEN))
     470        return;
     471
     472    /* check IP header */
     473    iphdr = (struct iphdr *) buf;
     474    hlen = iphdr->ihl << 2;
     475    sz -= hlen;
     476    icmppkt = (struct icmp *) (buf + hlen);
     477    if (icmppkt->icmp_id != myid)
     478        return;             /* not our ping */
     479
     480    if (icmppkt->icmp_type == ICMP_ECHOREPLY) {
     481        uint16_t recv_seq = ntohs(icmppkt->icmp_seq);
     482        uint32_t *tp = NULL;
     483
     484        if (sz >= ICMP_MINLEN + sizeof(uint32_t))
     485            tp = (uint32_t *) icmppkt->icmp_data;
     486        unpack_tail(sz, tp,
     487            inet_ntoa(*(struct in_addr *) &from->sin_addr.s_addr),
     488            recv_seq, iphdr->ttl);
     489    } else if (icmppkt->icmp_type != ICMP_ECHO) {
     490        bb_error_msg("warning: got ICMP %d (%s)",
     491                icmppkt->icmp_type,
     492                icmp_type_name(icmppkt->icmp_type));
     493    }
     494}
     495#if ENABLE_PING6
     496static void unpack6(char *packet, int sz, struct sockaddr_in6 *from, int hoplimit)
     497{
     498    struct icmp6_hdr *icmppkt;
     499    char buf[INET6_ADDRSTRLEN];
     500
     501    /* discard if too short */
     502    if (sz < (datalen + sizeof(struct icmp6_hdr)))
     503        return;
     504
     505    icmppkt = (struct icmp6_hdr *) packet;
     506    if (icmppkt->icmp6_id != myid)
     507        return;             /* not our ping */
     508
     509    if (icmppkt->icmp6_type == ICMP6_ECHO_REPLY) {
     510        uint16_t recv_seq = ntohs(icmppkt->icmp6_seq);
     511        uint32_t *tp = NULL;
     512
     513        if (sz >= sizeof(struct icmp6_hdr) + sizeof(uint32_t))
     514            tp = (uint32_t *) &icmppkt->icmp6_data8[4];
     515        unpack_tail(sz, tp,
     516            inet_ntop(AF_INET6, &pingaddr.sin6.sin6_addr,
     517                    buf, sizeof(buf)),
     518            recv_seq, hoplimit);
     519    } else if (icmppkt->icmp6_type != ICMP6_ECHO_REQUEST) {
     520        bb_error_msg("warning: got ICMP %d (%s)",
     521                icmppkt->icmp6_type,
     522                icmp6_type_name(icmppkt->icmp6_type));
     523    }
     524}
     525#endif
     526
     527static void ping4(len_and_sockaddr *lsa)
    329528{
    330529    char packet[datalen + MAXIPLEN + MAXICMPLEN];
     
    332531
    333532    pingsock = create_icmp_socket();
    334 
    335     memset(&pingaddr, 0, sizeof(struct sockaddr_in));
    336 
    337     pingaddr.sin_family = AF_INET;
    338     hostent = xgethostbyname(host);
    339     if (hostent->h_addrtype != AF_INET)
    340         bb_error_msg_and_die("unknown address type; only AF_INET is currently supported.");
    341 
    342     memcpy(&pingaddr.sin_addr, hostent->h_addr, sizeof(pingaddr.sin_addr));
     533    pingaddr.sin = lsa->sin;
     534    if (source_lsa) {
     535        if (setsockopt(pingsock, IPPROTO_IP, IP_MULTICAST_IF,
     536                &source_lsa->sa, source_lsa->len))
     537            bb_error_msg_and_die("can't set multicast source interface");
     538        xbind(pingsock, &source_lsa->sa, source_lsa->len);
     539    }
    343540
    344541    /* enable broadcast pings */
    345     sockopt = 1;
    346     setsockopt(pingsock, SOL_SOCKET, SO_BROADCAST, (char *) &sockopt,
    347                sizeof(sockopt));
     542    setsockopt_broadcast(pingsock);
    348543
    349544    /* set recv buf for broadcast pings */
    350     sockopt = 48 * 1024;
    351     setsockopt(pingsock, SOL_SOCKET, SO_RCVBUF, (char *) &sockopt,
    352                sizeof(sockopt));
    353 
    354     printf("PING %s (%s): %d data bytes\n",
    355                hostent->h_name,
    356            inet_ntoa(*(struct in_addr *) &pingaddr.sin_addr.s_addr),
    357            datalen);
     545    sockopt = 48 * 1024; /* explain why 48k? */
     546    setsockopt(pingsock, SOL_SOCKET, SO_RCVBUF, &sockopt, sizeof(sockopt));
    358547
    359548    signal(SIGINT, pingstats);
    360549
    361550    /* start the ping's going ... */
    362     sendping(0);
     551    sendping4(0);
    363552
    364553    /* listen for replies */
     
    368557        int c;
    369558
    370         if ((c = recvfrom(pingsock, packet, sizeof(packet), 0,
    371                           (struct sockaddr *) &from, &fromlen)) < 0) {
    372             if (errno == EINTR)
    373                 continue;
    374             bb_perror_msg("recvfrom");
     559        c = recvfrom(pingsock, packet, sizeof(packet), 0,
     560                (struct sockaddr *) &from, &fromlen);
     561        if (c < 0) {
     562            if (errno != EINTR)
     563                bb_perror_msg("recvfrom");
    375564            continue;
    376565        }
    377         unpack(packet, c, &from);
     566        unpack4(packet, c, &from);
    378567        if (pingcount > 0 && nreceived >= pingcount)
    379568            break;
    380569    }
     570}
     571#if ENABLE_PING6
     572extern int BUG_bad_offsetof_icmp6_cksum(void);
     573static void ping6(len_and_sockaddr *lsa)
     574{
     575    char packet[datalen + MAXIPLEN + MAXICMPLEN];
     576    int sockopt;
     577    struct msghdr msg;
     578    struct sockaddr_in6 from;
     579    struct iovec iov;
     580    char control_buf[CMSG_SPACE(36)];
     581
     582    pingsock = create_icmp6_socket();
     583    pingaddr.sin6 = lsa->sin6;
     584    /* untested whether "-I addr" really works for IPv6: */
     585    if (source_lsa)
     586        xbind(pingsock, &source_lsa->sa, source_lsa->len);
     587
     588#ifdef ICMP6_FILTER
     589    {
     590        struct icmp6_filter filt;
     591        if (!(option_mask32 & OPT_VERBOSE)) {
     592            ICMP6_FILTER_SETBLOCKALL(&filt);
     593            ICMP6_FILTER_SETPASS(ICMP6_ECHO_REPLY, &filt);
     594        } else {
     595            ICMP6_FILTER_SETPASSALL(&filt);
     596        }
     597        if (setsockopt(pingsock, IPPROTO_ICMPV6, ICMP6_FILTER, &filt,
     598                       sizeof(filt)) < 0)
     599            bb_error_msg_and_die("setsockopt(ICMP6_FILTER)");
     600    }
     601#endif /*ICMP6_FILTER*/
     602
     603    /* enable broadcast pings */
     604    setsockopt_broadcast(pingsock);
     605
     606    /* set recv buf for broadcast pings */
     607    sockopt = 48 * 1024; /* explain why 48k? */
     608    setsockopt(pingsock, SOL_SOCKET, SO_RCVBUF, &sockopt, sizeof(sockopt));
     609
     610    sockopt = offsetof(struct icmp6_hdr, icmp6_cksum);
     611    if (offsetof(struct icmp6_hdr, icmp6_cksum) != 2)
     612        BUG_bad_offsetof_icmp6_cksum();
     613    setsockopt(pingsock, SOL_RAW, IPV6_CHECKSUM, &sockopt, sizeof(sockopt));
     614
     615    /* request ttl info to be returned in ancillary data */
     616    setsockopt(pingsock, SOL_IPV6, IPV6_HOPLIMIT, &const_int_1, sizeof(const_int_1));
     617
     618    if (if_index)
     619        pingaddr.sin6.sin6_scope_id = if_index;
     620
     621    signal(SIGINT, pingstats);
     622
     623    /* start the ping's going ... */
     624    sendping6(0);
     625
     626    /* listen for replies */
     627    msg.msg_name = &from;
     628    msg.msg_namelen = sizeof(from);
     629    msg.msg_iov = &iov;
     630    msg.msg_iovlen = 1;
     631    msg.msg_control = control_buf;
     632    iov.iov_base = packet;
     633    iov.iov_len = sizeof(packet);
     634    while (1) {
     635        int c;
     636        struct cmsghdr *mp;
     637        int hoplimit = -1;
     638        msg.msg_controllen = sizeof(control_buf);
     639
     640        c = recvmsg(pingsock, &msg, 0);
     641        if (c < 0) {
     642            if (errno != EINTR)
     643                bb_perror_msg("recvfrom");
     644            continue;
     645        }
     646        for (mp = CMSG_FIRSTHDR(&msg); mp; mp = CMSG_NXTHDR(&msg, mp)) {
     647            if (mp->cmsg_level == SOL_IPV6
     648             && mp->cmsg_type == IPV6_HOPLIMIT
     649             /* don't check len - we trust the kernel: */
     650             /* && mp->cmsg_len >= CMSG_LEN(sizeof(int)) */
     651            ) {
     652                hoplimit = *(int*)CMSG_DATA(mp);
     653            }
     654        }
     655        unpack6(packet, c, &from, hoplimit);
     656        if (pingcount > 0 && nreceived >= pingcount)
     657            break;
     658    }
     659}
     660#endif
     661
     662static void ping(len_and_sockaddr *lsa)
     663{
     664    printf("PING %s (%s)", hostname, dotted);
     665    if (source_lsa) {
     666        printf(" from %s",
     667            xmalloc_sockaddr2dotted_noport(&source_lsa->sa));
     668    }
     669    printf(": %d data bytes\n", datalen);
     670
     671#if ENABLE_PING6
     672    if (lsa->sa.sa_family == AF_INET6)
     673        ping6(lsa);
     674    else
     675#endif
     676        ping4(lsa);
     677}
     678
     679int ping_main(int argc, char **argv);
     680int ping_main(int argc, char **argv)
     681{
     682    len_and_sockaddr *lsa;
     683    char *opt_c, *opt_s, *opt_I;
     684    USE_PING6(sa_family_t af = AF_UNSPEC;)
     685
     686    INIT_G();
     687
     688    datalen = DEFDATALEN; /* initialized here rather than in global scope to work around gcc bug */
     689
     690    /* exactly one argument needed, -v and -q don't mix */
     691    opt_complementary = "=1:q--v:v--q";
     692    getopt32(argv, OPT_STRING, &opt_c, &opt_s, &opt_I);
     693    if (option_mask32 & OPT_c) pingcount = xatoul(opt_c); // -c
     694    if (option_mask32 & OPT_s) datalen = xatou16(opt_s); // -s
     695    if (option_mask32 & OPT_I) { // -I
     696        if_index = if_nametoindex(opt_I);
     697        if (!if_index) {
     698            /* TODO: I'm not sure it takes IPv6 unless in [XX:XX..] format */
     699            source_lsa = xdotted2sockaddr(opt_I, 0);
     700        }
     701    }
     702    myid = (uint16_t) getpid();
     703    hostname = argv[optind];
     704#if ENABLE_PING6
     705    if (option_mask32 & OPT_IPV4)
     706        af = AF_INET;
     707    if (option_mask32 & OPT_IPV6)
     708        af = AF_INET6;
     709    lsa = xhost_and_af2sockaddr(hostname, 0, af);
     710#else
     711    lsa = xhost_and_af2sockaddr(hostname, 0, AF_INET);
     712#endif
     713
     714    if (source_lsa && source_lsa->sa.sa_family != lsa->sa.sa_family)
     715        /* leaking it here... */
     716        source_lsa = NULL;
     717
     718    dotted = xmalloc_sockaddr2dotted_noport(&lsa->sa);
     719    ping(lsa);
    381720    pingstats(0);
    382 }
    383 
    384 int ping_main(int argc, char **argv)
    385 {
    386     char *thisarg;
    387 
    388     datalen = DEFDATALEN; /* initialized here rather than in global scope to work around gcc bug */
    389 
    390     argc--;
    391     argv++;
    392     options = 0;
    393     /* Parse any options */
    394     while (argc >= 1 && **argv == '-') {
    395         thisarg = *argv;
    396         thisarg++;
    397         switch (*thisarg) {
    398         case 'q':
    399             options |= O_QUIET;
    400             break;
    401         case 'c':
    402             if (--argc <= 0)
    403                     bb_show_usage();
    404             argv++;
    405             pingcount = atoi(*argv);
    406             break;
    407         case 's':
    408             if (--argc <= 0)
    409                     bb_show_usage();
    410             argv++;
    411             datalen = atoi(*argv);
    412             break;
    413         default:
    414             bb_show_usage();
    415         }
    416         argc--;
    417         argv++;
    418     }
    419     if (argc < 1)
    420         bb_show_usage();
    421 
    422     myid = getpid() & 0xFFFF;
    423     ping(*argv);
    424721    return EXIT_SUCCESS;
    425722}
    426 #endif /* ! CONFIG_FEATURE_FANCY_PING */
     723#endif /* FEATURE_FANCY_PING */
     724
     725
     726#if ENABLE_PING6
     727int ping6_main(int argc, char **argv);
     728int ping6_main(int argc, char **argv)
     729{
     730    argv[0] = (char*)"-6";
     731    return ping_main(argc + 1, argv - 1);
     732}
     733#endif
     734
     735/* from ping6.c:
     736 * Copyright (c) 1989 The Regents of the University of California.
     737 * All rights reserved.
     738 *
     739 * This code is derived from software contributed to Berkeley by
     740 * Mike Muuss.
     741 *
     742 * Redistribution and use in source and binary forms, with or without
     743 * modification, are permitted provided that the following conditions
     744 * are met:
     745 * 1. Redistributions of source code must retain the above copyright
     746 *    notice, this list of conditions and the following disclaimer.
     747 * 2. Redistributions in binary form must reproduce the above copyright
     748 *    notice, this list of conditions and the following disclaimer in the
     749 *    documentation and/or other materials provided with the distribution.
     750 *
     751 * 3. <BSD Advertising Clause omitted per the July 22, 1999 licensing change
     752 *      ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change>
     753 *
     754 * 4. Neither the name of the University nor the names of its contributors
     755 *    may be used to endorse or promote products derived from this software
     756 *    without specific prior written permission.
     757 *
     758 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     759 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     760 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     761 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     762 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     763 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     764 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     765 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     766 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     767 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     768 * SUCH DAMAGE.
     769 */
Note: See TracChangeset for help on using the changeset viewer.