findInitPid() has been implemented and it seems to work.
[oweals/busybox.git] / ping.c
1 /*
2  * $Id: ping.c,v 1.5 1999/12/09 06:11:36 andersen Exp $
3  * Mini ping implementation for busybox
4  *
5  * Copyright (C) 1999 by Randolph Chung <tausq@debian.org>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  * General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20  *
21  * This version of ping is adapted from the ping in netkit-base 0.10,
22  * which is:
23  *
24  * Copyright (c) 1989 The Regents of the University of California.
25  * All rights reserved.
26  *
27  * This code is derived from software contributed to Berkeley by
28  * Mike Muuss.
29  * 
30  * Original copyright notice is retained at the end of this file.
31  */
32
33 #include "internal.h"
34 #include <sys/param.h>
35 #include <sys/socket.h>
36 #include <sys/file.h>
37 #include <sys/time.h>
38 #include <sys/times.h>
39 #include <sys/signal.h>
40
41 #include <netinet/in.h>
42 #include <netinet/ip.h>
43 #include <netinet/ip_icmp.h>
44 #include <arpa/inet.h>
45 #include <netdb.h>
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <errno.h>
49
50 #define DEFDATALEN      56
51 #define MAXIPLEN        60
52 #define MAXICMPLEN      76
53 #define MAXPACKET       65468
54 #define MAX_DUP_CHK     (8 * 128)
55 #define MAXWAIT         10
56 #define PINGINTERVAL    1 /* second */
57
58 #define O_QUIET         (1 << 0)
59
60 #define A(bit)          rcvd_tbl[(bit)>>3]      /* identify byte in array */
61 #define B(bit)          (1 << ((bit) & 0x07))   /* identify bit in byte */
62 #define SET(bit)        (A(bit) |= B(bit))
63 #define CLR(bit)        (A(bit) &= (~B(bit)))
64 #define TST(bit)        (A(bit) & B(bit))
65
66 static const char* ping_usage = "ping [OPTION]... host\n\n"
67 "Send ICMP ECHO_REQUEST packets to network hosts.\n\n"
68 "Options:\n"
69 "\t-q\t\tQuiet mode, only displays output at start and when finished.\n"
70 "\t-c COUNT\tSend only COUNT pings.\n";
71
72 static char *hostname = NULL;
73 static struct sockaddr_in pingaddr;
74 static int pingsock = -1;
75 static long ntransmitted = 0, nreceived = 0, nrepeats = 0, pingcount = 0;
76 static int myid = 0, options = 0;
77 static unsigned long tmin = ULONG_MAX, tmax = 0, tsum = 0;
78 static char rcvd_tbl[MAX_DUP_CHK / 8];
79
80 static void pingstats(int);
81 static void sendping(int);
82 static void unpack(char *, int, struct sockaddr_in *);
83 static void ping(char *);
84 static int in_cksum(unsigned short *, int);
85
86 /**************************************************************************/
87
88 static int in_cksum(unsigned short *buf, int sz)
89 {
90     int nleft = sz;
91     int sum = 0;
92     unsigned short *w = buf;
93     unsigned short ans = 0;
94
95     while (nleft > 1) {
96         sum += *w++;
97         nleft -= 2;
98     }
99
100     if (nleft == 1) { 
101         *(unsigned char *)(&ans) = *(unsigned char *)w;
102         sum += ans;
103     }
104
105     sum = (sum >> 16) + (sum & 0xFFFF);
106     sum += (sum >> 16);
107     ans = ~sum;
108     return(ans);
109 }   
110
111 static void pingstats(int ign) {
112     signal(SIGINT, SIG_IGN);
113     
114     printf("\n--- %s ping statistics ---\n", hostname);
115     printf("%ld packets transmitted, ", ntransmitted);
116     printf("%ld packets received, ", nreceived);
117     if (nrepeats)
118         printf("%ld duplicates, ", nrepeats);
119     if (ntransmitted)
120         printf("%ld%% packet loss\n", 
121                (ntransmitted - nreceived)*100/ntransmitted);
122     if (nreceived)
123         printf("round-trip min/avg/max = %lu.%lu/%lu.%lu/%lu.%lu ms\n",
124                tmin/10, tmin%10, 
125                (tsum/(nreceived+nrepeats))/10,
126                (tsum/(nreceived+nrepeats))%10,
127                tmax/10, tmax%10);
128     exit(0);
129 }
130
131 static void sendping(int ign)
132 {
133     struct icmp *pkt;
134     int i;
135     char packet[DEFDATALEN + 8];
136
137     pkt = (struct icmp *)packet;
138     
139     pkt->icmp_type = ICMP_ECHO;
140     pkt->icmp_code = 0;
141     pkt->icmp_cksum = 0;
142     pkt->icmp_seq = ntransmitted++;
143     pkt->icmp_id = myid; 
144     CLR(pkt->icmp_seq % MAX_DUP_CHK);
145
146     gettimeofday((struct timeval *)&packet[8], NULL);
147     pkt->icmp_cksum = in_cksum((unsigned short *)pkt, sizeof(packet));
148     
149     i = sendto(pingsock, packet, sizeof(packet), 0, 
150                (struct sockaddr *)&pingaddr, sizeof(struct sockaddr_in));
151
152     if (i < 0 || i != sizeof(packet)) {
153         if (i < 0) perror("ping");
154         fprintf(stderr, "ping wrote %d chars; %d expected\n", i, sizeof(packet));
155         exit(1);
156     }
157
158     signal(SIGALRM, sendping);
159     if (pingcount == 0 || ntransmitted < pingcount) { /* schedule next in 1s */
160         alarm(PINGINTERVAL);
161     } else { /* done, wait for the last ping to come back */
162         /* todo, don't necessarily need to wait so long... */
163         signal(SIGALRM, pingstats);
164         alarm(MAXWAIT);
165     }
166 }
167     
168 static void unpack(char *buf, int sz, struct sockaddr_in *from)
169 {
170     struct icmp *icmppkt;
171     struct iphdr *iphdr;
172     struct timeval tv, *tp;
173     int hlen, dupflag;
174     unsigned long triptime;
175     
176     gettimeofday(&tv, NULL);
177     
178     /* check IP header */
179     iphdr = (struct iphdr *)buf;
180     hlen = iphdr->ihl << 2;
181     /* discard if too short */
182     if (sz < (DEFDATALEN + ICMP_MINLEN)) return;
183
184     sz -= hlen;
185     icmppkt = (struct icmp *)(buf + hlen);
186     
187     if (icmppkt->icmp_type == ICMP_ECHOREPLY) {
188         if (icmppkt->icmp_id != myid) return; /* not our ping */
189         ++nreceived;
190         tp = (struct timeval *)icmppkt->icmp_data;
191
192         if ((tv.tv_usec -= tp->tv_usec) < 0) {
193             --tv.tv_sec;
194             tv.tv_usec += 1000000;
195         }
196         tv.tv_sec -= tp->tv_sec;
197         
198         triptime = tv.tv_sec * 10000 + (tv.tv_usec / 100);
199         tsum += triptime;
200         if (triptime < tmin) tmin = triptime;
201         if (triptime > tmax) tmax = triptime;
202
203         if (TST(icmppkt->icmp_seq % MAX_DUP_CHK)) {
204             ++nrepeats;
205             --nreceived;
206             dupflag = 1;
207         } else {
208             SET(icmppkt->icmp_seq % MAX_DUP_CHK);
209             dupflag = 0;
210         }
211
212         if (options & O_QUIET) return;
213
214         printf("%d bytes from %s: icmp_seq=%u", sz, 
215                inet_ntoa(*(struct in_addr *)&from->sin_addr.s_addr),
216                icmppkt->icmp_seq);
217         printf(" ttl=%d", iphdr->ttl);
218         printf(" time=%lu.%lu ms", triptime/10, triptime%10);
219         if (dupflag) printf(" (DUP!)");
220         printf("\n");
221     } else {
222         fprintf(stderr, "Warning: unknown ICMP packet received (not echo-reply)\n");
223     }   
224 }
225
226 static void ping(char *host)
227 {
228     struct protoent *proto;
229     struct hostent *h;
230     char buf[MAXHOSTNAMELEN];
231     char packet[DEFDATALEN + MAXIPLEN + MAXICMPLEN];
232     int sockopt;
233     
234     if (!(proto = getprotobyname("icmp"))) {
235         fprintf(stderr, "ping: unknown protocol icmp\n");
236         exit(1);
237     }
238     if ((pingsock = socket(AF_INET, SOCK_RAW, proto->p_proto)) < 0) {
239         if (errno == EPERM) {
240             fprintf(stderr, "ping: permission denied. (are you root?)\n");
241         } else {
242             perror("ping");
243         }
244         exit(1);
245     }
246
247     /* drop root privs if running setuid */
248     setuid(getuid());
249     
250     memset(&pingaddr, 0, sizeof(struct sockaddr_in));
251     pingaddr.sin_family = AF_INET;
252     if (inet_aton(host, &pingaddr.sin_addr)) {
253         hostname = host;
254     } else {    
255         if (!(h = gethostbyname(host))) {
256             fprintf(stderr, "ping: unknown host %s\n", host);
257             exit(1);
258         }
259
260         if (h->h_addrtype != AF_INET) {
261             fprintf(stderr, "ping: unknown address type; only AF_INET is currently supported.\n");
262             exit(1);
263         }
264         
265         pingaddr.sin_family = AF_INET; /* h->h_addrtype */
266         memcpy(&pingaddr.sin_addr, h->h_addr, sizeof(pingaddr.sin_addr)); 
267         strncpy(buf, h->h_name, sizeof(buf)-1);
268         hostname = buf;
269     }
270
271     /* enable broadcast pings */
272     sockopt = 1;
273     setsockopt(pingsock, SOL_SOCKET, SO_BROADCAST, (char *)&sockopt, sizeof(sockopt));
274     
275     /* set recv buf for broadcast pings */
276     sockopt = 48 * 1024;
277     setsockopt(pingsock, SOL_SOCKET, SO_RCVBUF, (char *)&sockopt, sizeof(sockopt));
278
279     printf("PING %s (%s): %d data bytes\n", 
280            hostname, inet_ntoa(*(struct in_addr *)&pingaddr.sin_addr.s_addr),
281            DEFDATALEN);
282
283     signal(SIGINT, pingstats);
284     
285     /* start the ping's going ... */
286     sendping(0);
287         
288     /* listen for replies */
289     while (1) {
290         struct sockaddr_in from;
291         size_t fromlen = sizeof(from);
292         int c;
293
294         if ((c = recvfrom(pingsock, packet, sizeof(packet), 0,
295                           (struct sockaddr *)&from, &fromlen)) < 0) {
296             if (errno == EINTR) continue;
297             perror("ping");
298             continue;
299         }
300         unpack(packet, c, &from);
301         if (pingcount > 0 && nreceived >= pingcount) break;     
302     }
303     pingstats(0);
304 }
305
306 extern int ping_main(int argc, char **argv)
307 {
308     char *thisarg;
309     
310     argc--;
311     argv++;
312     options = 0;
313     /* Parse any options */
314     while (argc > 1) {
315         if (**argv != '-') usage(ping_usage);
316         thisarg = *argv; thisarg++;
317         switch (*thisarg) {
318             case 'q': options |= O_QUIET; break;
319             case 'c':
320                 argc--; argv++;
321                 pingcount = atoi(*argv);                
322                 break;
323             default:
324                 usage(ping_usage);
325         }
326         argc--; argv++;
327     }
328     if (argc < 1) usage(ping_usage);       
329
330     myid = getpid() & 0xFFFF;
331     ping(*argv);
332     exit(TRUE);
333 }
334
335 /*
336  * Copyright (c) 1989 The Regents of the University of California.
337  * All rights reserved.
338  *
339  * This code is derived from software contributed to Berkeley by
340  * Mike Muuss.
341  *
342  * Redistribution and use in source and binary forms, with or without
343  * modification, are permitted provided that the following conditions
344  * are met:
345  * 1. Redistributions of source code must retain the above copyright
346  *    notice, this list of conditions and the following disclaimer.
347  * 2. Redistributions in binary form must reproduce the above copyright
348  *    notice, this list of conditions and the following disclaimer in the
349  *    documentation and/or other materials provided with the distribution.
350  * 3. All advertising materials mentioning features or use of this software
351  *    must display the following acknowledgement:
352  *      This product includes software developed by the University of
353  *      California, Berkeley and its contributors.
354  * 4. Neither the name of the University nor the names of its contributors
355  *    may be used to endorse or promote products derived from this software
356  *    without specific prior written permission.
357  *
358  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
359  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
360  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
361  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
362  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
363  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
364  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
365  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
366  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
367  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
368  * SUCH DAMAGE.
369  */
370
371