2 * Copyright (c) 1983, 1993
3 * The Regents of the University of California. All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by the University of
16 * California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 #include <sys/cdefs.h>
37 __unused
static const char copyright
[] =
38 "@(#) Copyright (c) 1983, 1993\n\
39 The Regents of the University of California. All rights reserved.\n";
42 /* Mac OS X kernel core dump server, based on the BSD trivial file
43 * transfer protocol server (FreeBSD distribution), with several
44 * modifications. This server is *not* compatible with tftp, as the
45 * protocol has changed considerably.
49 * Based on the trivial file transfer protocol server.
51 * The original version included many modifications by Jim Guyton
55 #include <sys/param.h>
56 #include <sys/ioctl.h>
58 #include <sys/socket.h>
59 #include <sys/types.h>
62 #include <netinet/in.h>
64 #include <arpa/inet.h>
80 #include <libkern/OSByteOrder.h>
82 #include "kdumpsubs.h"
84 #define DEFAULT_KDUMPD_PORTNO (1069)
88 int rexmtval
= TIMEOUT
;
89 int maxtimeout
= 25 * TIMEOUT
;
91 #define PKTSIZE SEGSIZE+6
93 char buf
[MAXIMUM_KDP_PKTSIZE
];
94 char ackbuf
[MAXIMUM_KDP_PKTSIZE
];
95 struct sockaddr_in from
;
98 void kdump
__P((struct kdumphdr
*, int));
101 * Null-terminated directory prefix list for absolute pathname requests and
102 * search list for relative pathname requests.
104 * MAXDIRS should be at least as large as the number of arguments that
105 * inetd allows (currently 20).
108 static struct dirlist
{
112 static int suppress_naks
;
113 static int logging
= 1;
115 static int server_mode
= 1;
117 static char *errtomsg
__P((int));
118 static void nak
__P((int));
119 static char * __P(verifyhost(struct sockaddr_in
*));
120 uint32_t kdp_crashdump_pkt_size
= (SEGSIZE
+ (sizeof(struct kdumphdr
)));
121 uint32_t kdp_crashdump_seg_size
= SEGSIZE
;
123 #define KDP_FEATURE_MASK_STRING "features"
124 enum {KDP_FEATURE_LARGE_CRASHDUMPS
= 1, KDP_FEATURE_LARGE_PKT_SIZE
= 2};
126 uint32_t kdp_crashdump_feature_mask
;
127 uint32_t kdp_feature_large_crashdumps
, kdp_feature_large_packets
;
134 register struct kdumphdr
*tp
;
137 struct sockaddr_in sin
;
138 char *chroot_dir
= NULL
;
139 struct passwd
*nobody
;
140 char *chuser
= "nobody";
142 openlog("kdumpd", LOG_PID
| LOG_NDELAY
, LOG_FTP
);
143 while ((ch
= getopt(argc
, argv
, "cClns:u:w")) != -1) {
167 syslog(LOG_WARNING
, "ignoring unknown option -%c", ch
);
172 struct dirlist
*dirp
;
174 /* Get list of directory prefixes. Skip relative pathnames. */
175 for (dirp
= dirs
; optind
< argc
&& dirp
< &dirs
[MAXDIRS
];
177 if (argv
[optind
][0] == '/') {
178 dirp
->name
= argv
[optind
];
179 dirp
->len
= strlen(dirp
->name
);
184 else if (chroot_dir
) {
188 if (ipchroot
&& chroot_dir
== NULL
) {
189 syslog(LOG_ERR
, "-c requires -s");
193 /* If we are not in server mode, skip the whole 'inetd' logic below. */
196 if (ioctl(0, FIONBIO
, &on
) < 0) {
197 syslog(LOG_ERR
, "ioctl(FIONBIO): %m");
200 fromlen
= sizeof (from
);
201 n
= recvfrom(0, buf
, sizeof (buf
), 0,
202 (struct sockaddr
*)&from
, &fromlen
);
204 syslog(LOG_ERR
, "recvfrom: %m");
208 * Now that we have read the message out of the UDP
209 * socket, we fork and exit. Thus, inetd will go back
210 * to listening to the kdump port, and the next request
211 * to come in will start up a new instance of kdumpd.
213 * We do this so that inetd can run kdumpd in "wait" mode.
214 * The problem with kdumpd running in "nowait" mode is that
215 * inetd may get one or more successful "selects" on the
216 * kdump port before we do our receive, so more than one
217 * instance of kdumpd may be started up. Worse, if kdumpd
218 * breaks before doing the above "recvfrom", inetd would
219 * spawn endless instances, clogging the system.
226 for (i
= 1; i
< 20; i
++) {
231 * flush out to most recently sent request.
233 * This may drop some requests, but those
234 * will be resent by the clients when
235 * they timeout. The positive effect of
236 * this flush is to (try to) prevent more
237 * than one kdumpd being started up to service
238 * a single request from a single client.
241 i
= recvfrom(0, buf
, sizeof (buf
), 0,
242 (struct sockaddr
*)&from
, &j
);
252 syslog(LOG_ERR
, "fork: %m");
254 } else if (pid
!= 0) {
261 * Since we exit here, we should do that only after the above
262 * recvfrom to keep inetd from constantly forking should there
263 * be a problem. See the above comment about system clogging.
267 char tempchroot
[MAXPATHLEN
];
272 tempaddr
= inet_ntoa(from
.sin_addr
);
273 snprintf(tempchroot
, sizeof(tempchroot
), "%s/%s", chroot_dir
, tempaddr
);
274 statret
= stat(tempchroot
, &sb
);
275 if (((sb
.st_mode
& S_IFMT
) == S_IFDIR
) &&
276 (statret
== 0 || (statret
== -1 && ipchroot
== 1)))
277 chroot_dir
= tempchroot
;
279 /* Must get this before chroot because /etc might go away */
280 if ((nobody
= getpwnam(chuser
)) == NULL
) {
281 syslog(LOG_ERR
, "%s: no such user", chuser
);
284 if (chroot(chroot_dir
)) {
285 syslog(LOG_ERR
, "chroot: %s: %m", chroot_dir
);
289 setuid(nobody
->pw_uid
);
290 } else if (0 != chdir(dirs
->name
)) {
291 syslog(LOG_ERR
, "chdir%s: %m", dirs
->name
);
294 from
.sin_family
= AF_INET
;
298 peer
= socket(AF_INET
, SOCK_DGRAM
, 0);
300 syslog(LOG_ERR
, "socket: %m");
303 memset(&sin
, 0, sizeof(sin
));
304 sin
.sin_family
= AF_INET
;
307 sin
.sin_addr
.s_addr
= htonl(INADDR_ANY
);
308 sin
.sin_port
= htons((uint16_t) DEFAULT_KDUMPD_PORTNO
);
311 if (bind(peer
, (struct sockaddr
*)&sin
, sizeof (sin
)) < 0) {
312 syslog(LOG_ERR
, "bind: %m");
318 * Wait for an incoming message from a remote peer, note that we need to
319 * populate n since kdump() expect the first message to be in buf
322 socklen_t slen
= sizeof(from
);
323 n
= recvfrom(peer
, buf
, sizeof(buf
), 0,
324 (struct sockaddr
*) &from
, &slen
);
326 syslog(LOG_ERR
, "recvfrom: %m");
331 if (connect(peer
, (struct sockaddr
*)&from
, sizeof(from
)) < 0) {
332 syslog(LOG_ERR
, "connect: %m");
335 tp
= (struct kdumphdr
*)buf
;
336 tp
->th_opcode
= ntohs(tp
->th_opcode
);
337 if (tp
->th_opcode
== WRQ
)
343 int validate_access
__P((char **, int));
345 void recvfile
__P((struct formats
*));
349 int (*f_validate
) __P((char **, int));
351 void (*f_recv
) __P((struct formats
*));
354 { "netascii", validate_access
, recvfile
, 1 },
355 { "octet", validate_access
, recvfile
, 0 },
360 * Handle initial connection protocol.
368 int first
= 1, ecode
;
369 register struct formats
*pf
;
370 char *filename
, *mode
= NULL
;
372 filename
= cp
= tp
->th_stuff
;
374 while (cp
< buf
+ size
) {
388 for (cp
= mode
; *cp
; cp
++)
393 if (strncmp(KDP_FEATURE_MASK_STRING
, cp
, sizeof(KDP_FEATURE_MASK_STRING
)) == 0) {
394 kdp_crashdump_feature_mask
= ntohl(*(uint32_t *) (cp
+ sizeof(KDP_FEATURE_MASK_STRING
)));
395 kdp_feature_large_crashdumps
= kdp_crashdump_feature_mask
& KDP_FEATURE_LARGE_CRASHDUMPS
;
396 kdp_feature_large_packets
= kdp_crashdump_feature_mask
& KDP_FEATURE_LARGE_PKT_SIZE
;
398 if (kdp_feature_large_packets
) {
399 kdp_crashdump_pkt_size
= KDP_LARGE_CRASHDUMP_PKT_SIZE
;
400 kdp_crashdump_seg_size
= kdp_crashdump_pkt_size
- sizeof(struct kdumphdr
);
402 syslog(KDUMPD_DEBUG_LEVEL
, "Received feature mask %s:0x%x", cp
, kdp_crashdump_feature_mask
);
404 syslog(KDUMPD_DEBUG_LEVEL
, "Unable to locate feature mask, mode: %s", mode
);
406 for (pf
= formats
; pf
->f_mode
; pf
++)
407 if (strcmp(pf
->f_mode
, mode
) == 0)
409 if (pf
->f_mode
== 0) {
413 ecode
= (*pf
->f_validate
)(&filename
, tp
->th_opcode
);
415 syslog(KDUMPD_DEBUG_LEVEL
, "%s: %s request for %s: %s", verifyhost(&from
),
416 tp
->th_opcode
== WRQ
? "write" : "read",
417 filename
, errtomsg(ecode
));
421 * Avoid storms of naks to a RRQ broadcast for a relative
422 * bootfile pathname from a diskless Sun.
424 if (suppress_naks
&& *filename
!= '/' && ecode
== ENOTFOUND
)
429 if (tp
->th_opcode
== WRQ
)
439 * Validate file access. We only allow storage of files that do not already
440 * exist, and that do not include directory specifiers in their pathnames.
441 * This is because kernel coredump filenames should always be of the form
442 * "core-version-IP as dotted quad-random string" as in :
443 * core-custom-17.202.40.204-a75b4eec
444 * The file is written to the directory supplied as the first argument
449 validate_access(char **filep
, int mode
)
453 char *filename
= *filep
;
454 static char pathname
[MAXPATHLEN
];
456 if (strstr(filename
, "/") || strstr(filename
, ".."))
459 snprintf(pathname
, sizeof(pathname
), "./%s", filename
);
461 if (0 == stat(pathname
, &stbuf
))
468 fd
= open(filename
, O_RDWR
|O_CREAT
|O_TRUNC
, S_IRUSR
| S_IWUSR
| S_IRGRP
| S_IWGRP
| S_IROTH
| S_IWOTH
);
471 return (errno
+ 100);
473 file
= fdopen(fd
, (mode
== RRQ
)? "r":"w");
489 if (timeout
>= maxtimeout
)
491 longjmp(timeoutbuf
, 2);
493 longjmp(timeoutbuf
, 1);
509 struct kdumphdr
*dp
, *w_init();
510 register struct kdumphdr
*ap
; /* ack buffer */
511 register int n
, size
;
512 volatile unsigned int block
;
513 volatile unsigned int jmpval
= 0;
515 signal(SIGALRM
, timer
);
517 ap
= (struct kdumphdr
*)ackbuf
;
520 send_seek_ack
: timeout
= 0;
522 ap
->th_opcode
= htons((u_short
)ACK
| ((kdp_feature_large_crashdumps
| kdp_feature_large_packets
) << 8));
524 ap
->th_opcode
= htons((u_short
)ACK
);
525 ap
->th_block
= htonl((unsigned int)block
);
527 jmpval
= setjmp(timeoutbuf
);
530 syslog (LOG_ERR
, "Timing out and flushing file to disk");
534 if (send(peer
, ackbuf
, 6 , 0) != 6) {
535 syslog(LOG_ERR
, "write: %m");
538 write_behind(file
, pf
->f_convert
);
541 n
= recv(peer
, dp
, kdp_crashdump_pkt_size
, 0);
543 if (n
< 0) { /* really? */
544 syslog(LOG_ERR
, "read: %m");
547 dp
->th_opcode
= ntohs((u_short
)dp
->th_opcode
);
548 dp
->th_block
= ntohl((unsigned int)dp
->th_block
);
550 syslog(KDUMPD_DEBUG_LEVEL
, "Received packet type %u, block %u\n", (unsigned)dp
->th_opcode
, (unsigned)dp
->th_block
);
553 if (dp
->th_opcode
== ERROR
)
556 if (dp
->th_opcode
== KDP_EOF
)
558 syslog (LOG_ERR
, "Received last panic dump packet");
561 if (dp
->th_opcode
== KDP_SEEK
)
563 if (dp
->th_block
== block
)
565 off_t crashdump_offset
= 0;
566 unsigned int tempoff
= 0;
568 if (kdp_feature_large_crashdumps
) {
569 crashdump_offset
= OSSwapBigToHostInt64((*(uint64_t *)dp
->th_data
));
572 bcopy (dp
->th_data
, &tempoff
, sizeof(unsigned int));
573 crashdump_offset
= ntohl(tempoff
);
577 syslog(KDUMPD_DEBUG_LEVEL
, "Seeking to offset 0x%llx\n", crashdump_offset
);
580 lseek(fileno (file
), crashdump_offset
, SEEK_SET
);
582 syslog (LOG_ERR
, "lseek: %m");
586 (void) synchnet(peer
);
587 if (dp
->th_block
== (block
-1))
589 syslog (LOG_DAEMON
|LOG_ERR
, "Retransmitting seek ack - current block %u, received block %u", block
, dp
->th_block
);
590 goto send_ack
; /* rexmit */
594 if (dp
->th_opcode
== DATA
) {
595 if (dp
->th_block
== block
) {
598 /* Re-synchronize with the other side */
599 (void) synchnet(peer
);
600 if (dp
->th_block
== (block
-1))
602 syslog (LOG_DAEMON
|LOG_ERR
, "Retransmitting ack - current block %u, received block %u", block
, dp
->th_block
);
603 goto send_ack
; /* rexmit */
606 syslog (LOG_DAEMON
|LOG_ERR
, "Not retransmitting ack - current block %u, received block %u", block
, dp
->th_block
);
610 syslog(KDUMPD_DEBUG_LEVEL
, "Writing block sized %u, current offset 0x%llx\n", n
- 6, ftello(file
));
612 size
= writeit(file
, &dp
, n
- 6, pf
->f_convert
);
613 if (size
!= (n
-6)) { /* ahem */
614 if (size
< 0) nak(errno
+ 100);
618 } while (dp
->th_opcode
!= KDP_EOF
);
621 ap
->th_opcode
= htons((u_short
)ACK
); /* send the "final" ack */
622 ap
->th_block
= htonl((unsigned int) (block
));
623 (void) send(peer
, ackbuf
, 6, 0);
625 write_behind(file
, pf
->f_convert
);
626 (void) fclose(file
); /* close data file */
627 syslog (LOG_ERR
, "file closed, sending final ACK\n");
629 signal(SIGALRM
, justquit
); /* just quit on timeout */
631 n
= recv(peer
, buf
, sizeof (buf
), 0); /* normally times out and quits */
633 if (n
>= 6 && /* if read some data */
634 dp
->th_opcode
== DATA
&& /* and got a data block */
635 block
== dp
->th_block
) { /* then my last ack was lost */
636 (void) send(peer
, ackbuf
, 6, 0); /* resend final ack */
642 /* update if needed, when adding new errmsgs */
643 #define MAXERRMSGLEN 40
649 { EUNDEF
, "Undefined error code" },
650 { ENOTFOUND
, "File not found" },
651 { EACCESS
, "Access violation" },
652 { ENOSPACE
, "Disk full or allocation exceeded" },
653 { EBADOP
, "Illegal KDUMP operation" },
654 { EBADID
, "Unknown transfer ID" },
655 { EEXISTS
, "File already exists" },
656 { ENOUSER
, "No such user" },
665 register struct errmsg
*pe
;
668 for (pe
= errmsgs
; pe
->e_code
>= 0; pe
++)
669 if (pe
->e_code
== error
)
671 snprintf(buf
, sizeof(buf
), "error %d", error
);
676 * Send a nak packet (error message).
677 * Error code passed in is one of the
678 * standard KDUMP codes, or a UNIX errno
685 register struct kdumphdr
*tp
;
687 register struct errmsg
*pe
;
689 tp
= (struct kdumphdr
*)buf
;
690 tp
->th_opcode
= htons((u_short
)ERROR
);
691 tp
->th_code
= htons((unsigned int)error
);
692 for (pe
= errmsgs
; pe
->e_code
>= 0; pe
++)
693 if (pe
->e_code
== error
)
695 if (pe
->e_code
< 0) {
696 pe
->e_msg
= strerror(error
- 100);
697 tp
->th_code
= EUNDEF
; /* set 'undef' errorcode */
699 if (strlen(pe
->e_msg
) > MAXERRMSGLEN
) {
700 syslog(LOG_ERR
, "nak: error msg too long");
704 strlcpy(tp
->th_msg
, pe
->e_msg
, MAXERRMSGLEN
);
705 length
= strlen(pe
->e_msg
);
706 tp
->th_msg
[length
] = '\0';
708 if (send(peer
, buf
, length
, 0) != length
)
709 syslog(LOG_ERR
, "nak: %m");
716 struct sockaddr_in
*fromp
;
720 hp
= gethostbyaddr((char *)&fromp
->sin_addr
, sizeof(fromp
->sin_addr
),
725 return inet_ntoa(fromp
->sin_addr
);