mirror of https://github.com/crash-utility/crash
3889 lines
104 KiB
C
3889 lines
104 KiB
C
/* remote.c - core analysis suite
|
||
*
|
||
* Copyright (C) 1999, 2000, 2001, 2002 Mission Critical Linux, Inc.
|
||
* Copyright (C) 2002, 2003, 2004, 2005, 2009, 2011, 2018 David Anderson
|
||
* Copyright (C) 2002, 2003, 2004, 2005, 2009, 2011, 2018 Red Hat, Inc. All rights reserved.
|
||
*
|
||
* This program is free software; you can redistribute it and/or modify
|
||
* it under the terms of the GNU General Public License as published by
|
||
* the Free Software Foundation; either version 2 of the License, or
|
||
* (at your option) any later version.
|
||
*
|
||
* This program is distributed in the hope that it will be useful,
|
||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
* GNU General Public License for more details.
|
||
*/
|
||
|
||
#include "defs.h"
|
||
#include <netinet/in.h>
|
||
#include <netdb.h>
|
||
#include <net/if_arp.h>
|
||
#include <sys/socket.h>
|
||
#include <arpa/inet.h>
|
||
#include <netinet/tcp.h>
|
||
#include <elf.h>
|
||
|
||
#define FAILMSG "FAIL "
|
||
#define DONEMSG "DONE "
|
||
#define DATAMSG "DATA "
|
||
|
||
#define DATA_HDRSIZE (13) /* strlen("XXXX ") + strlen("0131072") + NULL */
|
||
|
||
#define MAXRECVBUFSIZE (131072)
|
||
#define READBUFSIZE (MAXRECVBUFSIZE+DATA_HDRSIZE)
|
||
|
||
#ifdef DAEMON
|
||
/*
|
||
* The remote daemon.
|
||
*/
|
||
|
||
static int daemon_init(void);
|
||
static ulong daemon_htol(char *);
|
||
static int daemon_is_elf_file(char *);
|
||
static int daemon_mount_point(char *);
|
||
static int daemon_find_booted_kernel(char *);
|
||
static char **daemon_build_searchdirs(int);
|
||
static int daemon_is_directory(char *);
|
||
static int daemon_file_readable(char *);
|
||
static int daemon_parse_line(char *, char **);
|
||
static char *daemon_clean_line(char *);
|
||
int console(char *, ...);
|
||
static void daemon_socket_options(int);
|
||
static char *no_debugging_symbols_found(char *);
|
||
static ulong daemon_filesize(int);
|
||
static int daemon_find_module(char *, char *, char *);
|
||
static int daemon_search_directory_tree(char *, char *, char *);
|
||
static int daemon_file_exists(char *, struct stat *);
|
||
static int daemon_checksum(char *, long *);
|
||
static void daemon_send(void *, int);
|
||
static int daemon_proc_version(char *);
|
||
static void handle_connection(int);
|
||
|
||
struct remote_context {
|
||
int sock;
|
||
int remdebug;
|
||
char *remdebugfile;
|
||
} remote_context = { 0, 0, "/dev/null" };
|
||
|
||
struct remote_context *rc = &remote_context;
|
||
|
||
int
|
||
main(int argc, char **argv)
|
||
{
|
||
int c, sockfd, newsockfd, clilen;
|
||
struct sockaddr_in serv_addr, cli_addr;
|
||
struct hostent *hp;
|
||
ushort tcp_port;
|
||
char hostname[MAXHOSTNAMELEN];
|
||
|
||
tcp_port = 0;
|
||
optind = 0;
|
||
while ((c = getopt(argc, argv, "vd:")) > 0) {
|
||
switch (c)
|
||
{
|
||
case 'v':
|
||
printf("%s %s\n", basename(argv[0]),
|
||
/* BASELEVEL_REVISION */ "(deprecated)");
|
||
exit(0);
|
||
|
||
case 'd':
|
||
rc->remdebug++;
|
||
rc->remdebugfile = optarg;
|
||
break;
|
||
}
|
||
}
|
||
|
||
console("<parent daemon %d initiated>\n", getpid());
|
||
|
||
while (argv[optind]) {
|
||
if (!tcp_port)
|
||
tcp_port = (ushort)atoi(argv[optind]);
|
||
optind++;
|
||
}
|
||
|
||
console("port: %d\n", tcp_port);
|
||
|
||
if (gethostname(hostname, MAXHOSTNAMELEN) < 0) {
|
||
console("gethostname failed: %s\n", strerror(errno));
|
||
perror("gethostname");
|
||
exit(1);
|
||
}
|
||
|
||
console("hostname: %s\n", hostname);
|
||
|
||
if ((hp = gethostbyname(hostname)) == NULL) {
|
||
console("gethostbyname failed: %s\n", hstrerror(h_errno));
|
||
perror("gethostbyname");
|
||
exit(1);
|
||
}
|
||
|
||
console("attempting daemon_init...\n");
|
||
|
||
if (!daemon_init())
|
||
exit(1);
|
||
|
||
console("<daemon %d initiated>\n", getpid());
|
||
|
||
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
|
||
exit(1);
|
||
|
||
BZERO((char *)&serv_addr, sizeof(serv_addr));
|
||
serv_addr.sin_family = AF_INET;
|
||
BCOPY(hp->h_addr, (char *)&serv_addr.sin_addr, hp->h_length);
|
||
serv_addr.sin_port = htons(tcp_port);
|
||
|
||
daemon_socket_options(sockfd);
|
||
|
||
if (bind(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0){
|
||
console("%d: bind failed: %s\n", getpid(), strerror(errno));
|
||
exit(1);
|
||
}
|
||
|
||
if (listen(sockfd, 5) < 0) {
|
||
console("%d: listen failed: %s\n", getpid(), strerror(errno));
|
||
exit(1);
|
||
}
|
||
|
||
for (;;) {
|
||
clilen = sizeof(cli_addr);
|
||
|
||
if ((newsockfd = accept(sockfd, (struct sockaddr *)&cli_addr,
|
||
&clilen)) < 0) {
|
||
console("%d: accept failed: %s\n", getpid(),
|
||
strerror(errno));
|
||
exit(1);
|
||
}
|
||
|
||
switch (fork())
|
||
{
|
||
case -1: exit(1);
|
||
|
||
case 0: close(sockfd);
|
||
handle_connection(newsockfd);
|
||
exit(0);
|
||
|
||
default:
|
||
close(newsockfd);
|
||
break;
|
||
}
|
||
|
||
close(newsockfd);
|
||
}
|
||
}
|
||
|
||
/*
|
||
* This probably doesn't do much, but it might reduce the acknowledge
|
||
* negotiations somewhat. (?)
|
||
*/
|
||
static void
|
||
daemon_socket_options(int sockfd)
|
||
{
|
||
int nodelay;
|
||
|
||
if (setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY,
|
||
(char *)&nodelay, sizeof(nodelay)) < 0)
|
||
console("TCP_NODELAY setsockopt error\n");
|
||
}
|
||
|
||
/*
|
||
* This is the child daemon that handles the incoming requests.
|
||
*/
|
||
#define MAX_REMOTE_FDS (10)
|
||
|
||
static void
|
||
handle_connection(int sock)
|
||
{
|
||
int i;
|
||
char recvbuf[BUFSIZE];
|
||
char savebuf[BUFSIZE];
|
||
char sendbuf[BUFSIZE];
|
||
char buf1[BUFSIZE];
|
||
char readbuf[READBUFSIZE+1];
|
||
char *file;
|
||
FILE *tmp, *pipe;
|
||
char *p1, *p2, *p3;
|
||
size_t cnt;
|
||
int fds[MAX_REMOTE_FDS];
|
||
int mfd;
|
||
ulong addr, total, reqsize, bufsize;
|
||
fd_set rfds;
|
||
int len, first, retval, done;
|
||
struct stat sbuf;
|
||
|
||
rc->sock = sock;
|
||
|
||
console("< new connection >\n");
|
||
|
||
for (i = 0; i < MAX_REMOTE_FDS; i++)
|
||
fds[i] = -1;
|
||
|
||
while (TRUE) {
|
||
|
||
FD_ZERO(&rfds);
|
||
FD_SET(sock, &rfds);
|
||
retval = select(sock+1, &rfds, NULL, NULL, NULL);
|
||
|
||
BZERO(sendbuf, BUFSIZE);
|
||
BZERO(recvbuf, BUFSIZE);
|
||
|
||
switch (read(sock, recvbuf, BUFSIZE-1))
|
||
{
|
||
case -1:
|
||
console("[read returned -1]\n");
|
||
continue;
|
||
case 0:
|
||
console("[read returned 0]\n");
|
||
return;
|
||
default:
|
||
console("[%s]: ", recvbuf);
|
||
break;
|
||
}
|
||
|
||
if (STRNEQ(recvbuf, "OPEN ")) {
|
||
|
||
strcpy(sendbuf, recvbuf);
|
||
p1 = strtok(recvbuf, " "); /* OPEN */
|
||
file = strtok(NULL, " "); /* filename */
|
||
|
||
for (i = 0; i < MAX_REMOTE_FDS; i++) {
|
||
if (fds[i] == -1)
|
||
break;
|
||
}
|
||
|
||
if (i < MAX_REMOTE_FDS) {
|
||
if ((fds[i] = open(file, O_RDWR)) < 0) {
|
||
if ((fds[i] = open(file, O_RDONLY)) < 0)
|
||
strcat(sendbuf, " <FAIL>");
|
||
else {
|
||
sprintf(buf1,
|
||
" %d O_RDONLY %ld", fds[i],
|
||
daemon_filesize(fds[i]));
|
||
strcat(sendbuf, buf1);
|
||
}
|
||
} else {
|
||
sprintf(buf1, " %d O_RDWR %ld", fds[i],
|
||
daemon_filesize(fds[i]));
|
||
strcat(sendbuf, buf1);
|
||
}
|
||
} else
|
||
strcat(sendbuf, " <FAIL>");
|
||
|
||
console("[%s]\n", sendbuf);
|
||
|
||
daemon_send(sendbuf, strlen(sendbuf));
|
||
continue;
|
||
|
||
} else if (STRNEQ(recvbuf, "READ_LIVE ")) {
|
||
|
||
strcpy(savebuf, recvbuf);
|
||
|
||
p1 = strtok(recvbuf, " "); /* READ_LIVE */
|
||
p1 = strtok(NULL, " "); /* filename id */
|
||
p2 = strtok(NULL, " "); /* address */
|
||
p3 = strtok(NULL, " "); /* length */
|
||
|
||
addr = daemon_htol(p2);
|
||
len = atoi(p3);
|
||
mfd = atoi(p1);
|
||
errno = 0;
|
||
|
||
BZERO(readbuf, READBUFSIZE);
|
||
|
||
if (lseek(mfd, addr, SEEK_SET) == -1)
|
||
len = 0;
|
||
else if (read(mfd, &readbuf[DATA_HDRSIZE], len) != len)
|
||
len = 0;
|
||
|
||
if (!len) {
|
||
sprintf(readbuf, "%s%07ld", FAILMSG,
|
||
(ulong)errno);
|
||
console("[%s]\n", readbuf);
|
||
} else {
|
||
sprintf(readbuf, "%s%07ld", DONEMSG,(ulong)len);
|
||
console("(%ld)\n", len);
|
||
}
|
||
|
||
daemon_send(readbuf, len+DATA_HDRSIZE);
|
||
|
||
continue;
|
||
|
||
} else if (STRNEQ(recvbuf, "READ_NETDUMP ")) {
|
||
|
||
strcpy(savebuf, recvbuf);
|
||
p1 = strtok(recvbuf, " "); /* READ_NETDUMP */
|
||
p2 = strtok(NULL, " "); /* address */
|
||
p3 = strtok(NULL, " "); /* length */
|
||
|
||
addr = daemon_htol(p2);
|
||
len = atoi(p3);
|
||
|
||
BZERO(readbuf, READBUFSIZE);
|
||
errno = 0;
|
||
|
||
if ((len = read_netdump(UNUSED,
|
||
&readbuf[DATA_HDRSIZE], len, UNUSED, addr)) < 0)
|
||
len = 0;
|
||
|
||
if (len) {
|
||
sprintf(readbuf, "%s%07ld", DONEMSG,(ulong)len); console("(%ld)\n", (ulong)len);
|
||
} else {
|
||
sprintf(readbuf, "%s%07ld", FAILMSG,
|
||
(ulong)errno);
|
||
console("[%s]\n", readbuf);
|
||
}
|
||
|
||
daemon_send(readbuf, len+DATA_HDRSIZE);
|
||
continue;
|
||
|
||
} else if (STRNEQ(recvbuf, "READ_MCLXCD ")) {
|
||
|
||
strcpy(savebuf, recvbuf);
|
||
p1 = strtok(recvbuf, " "); /* READ_MCLXCD */
|
||
p2 = strtok(NULL, " "); /* address */
|
||
p3 = strtok(NULL, " "); /* length */
|
||
|
||
addr = daemon_htol(p2);
|
||
len = atoi(p3);
|
||
errno = 0;
|
||
|
||
BZERO(readbuf, READBUFSIZE);
|
||
|
||
if (vas_lseek(addr, SEEK_SET))
|
||
len = 0;
|
||
else if (vas_read((void *)
|
||
&readbuf[DATA_HDRSIZE], len) != len)
|
||
len = 0;
|
||
|
||
if (len) {
|
||
sprintf(readbuf, "%s%07ld", DONEMSG,
|
||
(ulong)len);
|
||
console("(%ld)\n", (ulong)len);
|
||
} else {
|
||
sprintf(readbuf, "%s%07ld", FAILMSG,
|
||
(ulong)errno);
|
||
console("[%s]\n", readbuf);
|
||
}
|
||
|
||
daemon_send(readbuf, len+DATA_HDRSIZE);
|
||
|
||
continue;
|
||
|
||
} else if (STRNEQ(recvbuf, "CLOSE ")) {
|
||
|
||
strcpy(savebuf, recvbuf);
|
||
p1 = strtok(recvbuf, " "); /* SIZE */
|
||
p1 = strtok(NULL, " "); /* filename id */
|
||
mfd = atoi(p1);
|
||
|
||
for (i = retval = 0; i < MAX_REMOTE_FDS; i++) {
|
||
if (fds[i] == mfd) {
|
||
close(mfd);
|
||
fds[i] = -1;
|
||
retval = TRUE;
|
||
break;
|
||
}
|
||
}
|
||
|
||
sprintf(sendbuf, "%s%s", savebuf,
|
||
retval ? " OK" : " <FAIL>");
|
||
console("[%s]\n", sendbuf);
|
||
|
||
daemon_send(sendbuf, strlen(sendbuf));
|
||
continue;
|
||
|
||
} else if (STRNEQ(recvbuf, "READ ")) {
|
||
|
||
strcpy(savebuf, recvbuf);
|
||
|
||
p1 = strtok(recvbuf, " "); /* READ */
|
||
p1 = strtok(NULL, " "); /* filename id */
|
||
p2 = strtok(NULL, " "); /* address */
|
||
p3 = strtok(NULL, " "); /* length */
|
||
|
||
addr = daemon_htol(p2);
|
||
len = atoi(p3);
|
||
mfd = atoi(p1);
|
||
|
||
BZERO(readbuf, READBUFSIZE);
|
||
|
||
if (lseek(mfd, addr, SEEK_SET) == -1)
|
||
len = 0;
|
||
else if (read(mfd, readbuf, len) != len)
|
||
len = 0;
|
||
|
||
if (!len) {
|
||
sprintf(readbuf, "%s <FAIL>", savebuf);
|
||
len = strlen(readbuf);
|
||
console("[%s]\n", readbuf);
|
||
} else
|
||
console("(%ld)\n", len);
|
||
|
||
daemon_send(readbuf, len);
|
||
|
||
continue;
|
||
|
||
} else if (STRNEQ(recvbuf, "MACHINE_PID")) {
|
||
|
||
sprintf(sendbuf, "%s %s %d",
|
||
recvbuf, MACHINE_TYPE, getpid());
|
||
console("[%s]\n", sendbuf);
|
||
daemon_send(sendbuf, strlen(sendbuf));
|
||
continue;
|
||
|
||
} else if (STRNEQ(recvbuf, "TYPE ")) {
|
||
|
||
strcpy(savebuf, recvbuf);
|
||
p1 = strtok(recvbuf, " "); /* TYPE */
|
||
file = strtok(NULL, " "); /* filename */
|
||
|
||
if (stat(file, &sbuf) < 0)
|
||
sprintf(sendbuf, "%s <FAIL>", savebuf);
|
||
else if (daemon_is_elf_file(file))
|
||
sprintf(sendbuf, "%s ELF", savebuf);
|
||
else if (STREQ(file, "/dev/mem"))
|
||
sprintf(sendbuf, "%s DEVMEM", savebuf);
|
||
else if (is_netdump(file, NETDUMP_REMOTE))
|
||
sprintf(sendbuf, "%s NETDUMP", savebuf);
|
||
else if (is_mclx_compressed_dump(file))
|
||
sprintf(sendbuf, "%s MCLXCD", savebuf);
|
||
else if (is_lkcd_compressed_dump(file))
|
||
sprintf(sendbuf, "%s LKCD", savebuf);
|
||
else if (is_s390_dump(file))
|
||
sprintf(sendbuf, "%s S390D", savebuf);
|
||
else
|
||
sprintf(sendbuf, "%s UNSUPPORTED", savebuf);
|
||
|
||
console("[%s]\n", sendbuf);
|
||
|
||
daemon_send(sendbuf, strlen(sendbuf));
|
||
continue;
|
||
|
||
} else if (STRNEQ(recvbuf, "LINUX_VERSION ")) {
|
||
|
||
strcpy(savebuf, recvbuf);
|
||
p1 = strtok(recvbuf, " "); /* LINUX_VERSION */
|
||
file = strtok(NULL, " "); /* filename */
|
||
|
||
sprintf(readbuf,
|
||
"/usr/bin/strings %s | grep 'Linux version'",
|
||
file);
|
||
|
||
if ((pipe = popen(readbuf, "r"))) {
|
||
BZERO(readbuf, BUFSIZE);
|
||
if (fread(readbuf, sizeof(char), BUFSIZE-1,
|
||
pipe) > 0)
|
||
strcpy(sendbuf, readbuf);
|
||
else
|
||
sprintf(sendbuf, "%s <FAIL>", savebuf);
|
||
pclose(pipe);
|
||
} else
|
||
sprintf(sendbuf, "%s <FAIL>", savebuf);
|
||
|
||
console("[%s] (%d)\n", sendbuf, strlen(sendbuf));
|
||
|
||
daemon_send(sendbuf, strlen(sendbuf));
|
||
continue;
|
||
|
||
} else if (STRNEQ(recvbuf, "READ_GZIP ")) {
|
||
|
||
strcpy(savebuf, recvbuf);
|
||
p1 = strtok(recvbuf, " "); /* READ_GZIP */
|
||
p1 = strtok(NULL, " "); /* bufsize */
|
||
bufsize = atol(p1);
|
||
file = strtok(NULL, " "); /* filename */
|
||
|
||
errno = 0;
|
||
reqsize = bufsize - DATA_HDRSIZE;
|
||
|
||
sprintf(readbuf, "/usr/bin/gzip -c %s", file);
|
||
|
||
if ((pipe = popen(readbuf, "r")) == NULL) {
|
||
sprintf(readbuf, "%s%07ld", FAILMSG,
|
||
(ulong)errno);
|
||
console("[%s]\n", readbuf);
|
||
daemon_send(readbuf, DATA_HDRSIZE);
|
||
continue;
|
||
}
|
||
|
||
errno = cnt = done = total = first = 0;
|
||
|
||
while (!done) {
|
||
BZERO(readbuf, READBUFSIZE);
|
||
|
||
cnt = fread(&readbuf[DATA_HDRSIZE],
|
||
sizeof(char), reqsize, pipe);
|
||
|
||
total += cnt;
|
||
|
||
if (feof(pipe)) {
|
||
sprintf(readbuf, "%s%07ld",
|
||
DONEMSG, (ulong)cnt);
|
||
done = TRUE;
|
||
} else if (ferror(pipe)) {
|
||
sprintf(readbuf, "%s%07ld", FAILMSG,
|
||
(ulong)errno);
|
||
done = TRUE;
|
||
} else
|
||
sprintf(readbuf, "%s%07ld",
|
||
DATAMSG, (ulong)cnt);
|
||
|
||
console("%s[%s]\n", !first++ ? "\n" : "",
|
||
readbuf);
|
||
|
||
daemon_send(readbuf, bufsize);
|
||
}
|
||
|
||
console("GZIP total: %ld\n", total);
|
||
|
||
pclose(pipe);
|
||
continue;
|
||
|
||
} else if (STRNEQ(recvbuf, "PROC_VERSION")) {
|
||
|
||
BZERO(readbuf, READBUFSIZE);
|
||
|
||
if (!daemon_proc_version(readbuf))
|
||
sprintf(readbuf, "%s <FAIL>", recvbuf);
|
||
|
||
console("[%s]\n", readbuf);
|
||
|
||
daemon_send(readbuf, strlen(readbuf));
|
||
continue;
|
||
|
||
} else if (STRNEQ(recvbuf, "DEBUGGING_SYMBOLS ")) {
|
||
|
||
strcpy(savebuf, recvbuf);
|
||
p1 = strtok(recvbuf, " "); /* DEBUGGING */
|
||
p2 = strtok(NULL, " "); /* filename */
|
||
|
||
sprintf(sendbuf, "%s %s", savebuf,
|
||
no_debugging_symbols_found(p2));
|
||
console("[%s]\n", sendbuf);
|
||
|
||
daemon_send(sendbuf, strlen(sendbuf));
|
||
continue;
|
||
|
||
} else if (STRNEQ(recvbuf, "PAGESIZE ")) {
|
||
|
||
if (strstr(recvbuf, "LIVE"))
|
||
sprintf(sendbuf, "%s %d", recvbuf,
|
||
(uint)getpagesize());
|
||
else if (strstr(recvbuf, "NETDUMP"))
|
||
sprintf(sendbuf, "%s %d", recvbuf,
|
||
(uint)netdump_page_size());
|
||
else if (strstr(recvbuf, "MCLXCD"))
|
||
sprintf(sendbuf, "%s %d", recvbuf,
|
||
(uint)mclx_page_size());
|
||
else if (strstr(recvbuf, "LKCD"))
|
||
sprintf(sendbuf, "%s %d", recvbuf,
|
||
(uint)lkcd_page_size());
|
||
else if (strstr(recvbuf, "S390D"))
|
||
sprintf(sendbuf, "%s %d", recvbuf,
|
||
s390_page_size());
|
||
|
||
console("[%s]\n", sendbuf);
|
||
daemon_send(sendbuf, strlen(sendbuf));
|
||
continue;
|
||
|
||
} else if (STRNEQ(recvbuf, "FIND_BOOTED_KERNEL")) {
|
||
|
||
BZERO(readbuf, READBUFSIZE);
|
||
if (daemon_find_booted_kernel(readbuf))
|
||
sprintf(sendbuf, "%s %s", recvbuf, readbuf);
|
||
else
|
||
sprintf(sendbuf, "%s <FAIL>", recvbuf);
|
||
|
||
console("[%s]\n", sendbuf);
|
||
daemon_send(sendbuf, strlen(sendbuf));
|
||
continue;
|
||
|
||
} else if (STRNEQ(recvbuf, "FIND_MODULE ")) {
|
||
|
||
strcpy(savebuf, recvbuf);
|
||
|
||
strtok(recvbuf, " "); /* FIND_MODULE */
|
||
p1 = strtok(NULL, " "); /* release */
|
||
p2 = strtok(NULL, " "); /* module */
|
||
|
||
if (daemon_find_module(p1, p2, buf1)) {
|
||
if (daemon_checksum(buf1, &total))
|
||
sprintf(sendbuf, "%s %s %lx",
|
||
savebuf, buf1, total);
|
||
else
|
||
sprintf(sendbuf, "%s %s %lx",
|
||
savebuf, buf1,
|
||
(ulong)0xdeadbeef);
|
||
} else
|
||
sprintf(sendbuf, "%s <FAIL>", savebuf);
|
||
|
||
console("[%s]\n", sendbuf);
|
||
daemon_send(sendbuf, strlen(sendbuf));
|
||
continue;
|
||
|
||
} else if (STRNEQ(recvbuf, "SUM ")) {
|
||
|
||
strcpy(savebuf, recvbuf);
|
||
p1 = strtok(recvbuf, " "); /* SUM */
|
||
p2 = strtok(NULL, " "); /* filename */
|
||
|
||
if (daemon_checksum(p2, &total))
|
||
sprintf(sendbuf, "%s %lx", savebuf, total);
|
||
else
|
||
sprintf(sendbuf, "%s <FAIL>", savebuf);
|
||
|
||
console("[%s]\n", sendbuf);
|
||
daemon_send(sendbuf, strlen(sendbuf));
|
||
continue;
|
||
|
||
} else if (STRNEQ(recvbuf, "MEMORY ")) {
|
||
|
||
strcpy(savebuf, recvbuf);
|
||
p1 = strtok(recvbuf, " "); /* MEMORY */
|
||
p2 = strtok(NULL, " "); /* USED or FREE */
|
||
p3 = strtok(NULL, " "); /* MCLXCD, LKCD, etc. */
|
||
|
||
if (STREQ(p2, "FREE")) {
|
||
if (STREQ(p3, "NETDUMP"))
|
||
retval = netdump_free_memory();
|
||
else if (STREQ(p3, "MCLXCD"))
|
||
retval = vas_free_memory(NULL);
|
||
else if (STREQ(p3, "LKCD"))
|
||
retval = lkcd_free_memory();
|
||
else if (STREQ(p3, "S390D"))
|
||
retval = s390_free_memory();
|
||
}
|
||
|
||
if (STREQ(p2, "USED")) {
|
||
if (STREQ(p3, "NETDUMP"))
|
||
retval = netdump_memory_used();
|
||
else if (STREQ(p3, "MCLXCD"))
|
||
retval = vas_memory_used();
|
||
else if (STREQ(p3, "LKCD"))
|
||
retval = lkcd_memory_used();
|
||
else if (STREQ(p3, "S390D"))
|
||
retval = s390_memory_used();
|
||
}
|
||
|
||
sprintf(sendbuf, "%s %d", savebuf, retval);
|
||
console("[%s]\n", sendbuf);
|
||
daemon_send(sendbuf, strlen(sendbuf));
|
||
continue;
|
||
|
||
} else if (STRNEQ(recvbuf, "MEMORY_DUMP")) {
|
||
|
||
strcpy(savebuf, recvbuf);
|
||
p1 = strtok(recvbuf, " "); /* MEMORY_DUMP */
|
||
p1 = strtok(NULL, " "); /* bufsize */
|
||
p2 = strtok(NULL, " "); /* MCLXCD, LKCD, etc. */
|
||
bufsize = atol(p1);
|
||
|
||
reqsize = bufsize - DATA_HDRSIZE;
|
||
errno = 0;
|
||
|
||
if ((tmp = tmpfile()) == NULL) {
|
||
sprintf(readbuf, "%s%07ld", FAILMSG,
|
||
(ulong)errno);
|
||
console("[%s]\n", readbuf);
|
||
daemon_send(readbuf, DATA_HDRSIZE);
|
||
continue;
|
||
}
|
||
|
||
if (STREQ(p2, "NETDUMP"))
|
||
retval = netdump_memory_dump(tmp);
|
||
else if (STREQ(p2, "MCLXCD"))
|
||
vas_memory_dump(tmp);
|
||
else if (STREQ(p2, "LKCD"))
|
||
lkcd_memory_dump(tmp);
|
||
else if (STREQ(p2, "LKCD_VERBOSE")) {
|
||
set_lkcd_fp(tmp);
|
||
dump_lkcd_environment(0);
|
||
set_lkcd_fp(NULL);
|
||
} else if (STREQ(p2, "S390D"))
|
||
s390_memory_dump(tmp);
|
||
|
||
rewind(tmp);
|
||
errno = cnt = done = total = first = 0;
|
||
|
||
while (!done) {
|
||
BZERO(readbuf, READBUFSIZE);
|
||
|
||
cnt = fread(&readbuf[DATA_HDRSIZE],
|
||
sizeof(char), reqsize, tmp);
|
||
|
||
total += cnt;
|
||
|
||
if (feof(tmp)) {
|
||
sprintf(readbuf, "%s%07ld",
|
||
DONEMSG, (ulong)cnt);
|
||
done = TRUE;
|
||
} else if (ferror(tmp)) {
|
||
sprintf(readbuf, "%s%07ld", FAILMSG,
|
||
(ulong)errno);
|
||
done = TRUE;
|
||
} else
|
||
sprintf(readbuf, "%s%07ld",
|
||
DATAMSG, (ulong)cnt);
|
||
|
||
console("%s[%s]\n", !first++ ? "\n" : "",
|
||
readbuf);
|
||
daemon_send(readbuf, bufsize);
|
||
}
|
||
|
||
console("MEMORY_DUMP total: %ld\n", total);
|
||
|
||
fclose(tmp);
|
||
continue;
|
||
|
||
} else if (STRNEQ(recvbuf, "NETDUMP_INIT ")) {
|
||
|
||
strcpy(savebuf, recvbuf);
|
||
p1 = strtok(recvbuf, " "); /* NETDUMP_INIT */
|
||
p2 = strtok(NULL, " "); /* fd */
|
||
p3 = strtok(NULL, " "); /* dumpfile */
|
||
|
||
mfd = atoi(p2);
|
||
for (i = 0; i < MAX_REMOTE_FDS; i++) {
|
||
if (fds[i] == mfd) {
|
||
close(mfd);
|
||
fds[i] = -1;
|
||
break;
|
||
}
|
||
}
|
||
|
||
sprintf(sendbuf, "%s %s", savebuf,
|
||
netdump_init(p3, NULL) ? "OK" : "<FAIL>");
|
||
|
||
if ((addr = get_netdump_panic_task())) {
|
||
sprintf(readbuf, "\npanic_task: %lx\n", addr);
|
||
strcat(sendbuf, readbuf);
|
||
}
|
||
|
||
console("[%s]\n", sendbuf);
|
||
daemon_send(sendbuf, strlen(sendbuf));
|
||
continue;
|
||
|
||
} else if (STRNEQ(recvbuf, "LKCD_DUMP_INIT ")) {
|
||
|
||
strcpy(savebuf, recvbuf);
|
||
p1 = strtok(recvbuf, " "); /* LKCD_DUMP_INIT */
|
||
p2 = strtok(NULL, " "); /* fd */
|
||
p3 = strtok(NULL, " "); /* dumpfile */
|
||
|
||
sprintf(sendbuf, "%s %s", savebuf,
|
||
lkcd_dump_init(NULL, atoi(p2), p3) ?
|
||
"OK" : "<FAIL>");
|
||
|
||
if ((addr = get_lkcd_panic_task())) {
|
||
sprintf(readbuf, "\npanic_task: %lx\n", addr);
|
||
strcat(sendbuf, readbuf);
|
||
}
|
||
readbuf[0] = NULLCHAR;
|
||
get_lkcd_panicmsg(readbuf);
|
||
if (strlen(readbuf)) {
|
||
strcat(sendbuf, "panicmsg: ");
|
||
strcat(sendbuf, readbuf);
|
||
}
|
||
|
||
console("[%s]\n", sendbuf);
|
||
daemon_send(sendbuf, strlen(sendbuf));
|
||
continue;
|
||
|
||
} else if (STRNEQ(recvbuf, "READ_LKCD ")) {
|
||
|
||
strcpy(savebuf, recvbuf);
|
||
|
||
p1 = strtok(recvbuf, " "); /* READ_LKCD */
|
||
p1 = strtok(NULL, " "); /* filename id */
|
||
p2 = strtok(NULL, " "); /* address */
|
||
p3 = strtok(NULL, " "); /* length */
|
||
|
||
mfd = atoi(p1);
|
||
addr = daemon_htol(p2);
|
||
len = atoi(p3);
|
||
|
||
BZERO(readbuf, READBUFSIZE);
|
||
errno = 0;
|
||
|
||
if (!lkcd_lseek(addr))
|
||
len = 0;
|
||
else if (lkcd_read((void *)
|
||
&readbuf[DATA_HDRSIZE], len) != len)
|
||
len = 0;
|
||
|
||
if (len) {
|
||
sprintf(readbuf, "%s%07ld", DONEMSG,(ulong)len);
|
||
console("(%ld)\n", (ulong)len);
|
||
} else {
|
||
sprintf(readbuf, "%s%07ld", FAILMSG,
|
||
(ulong)errno);
|
||
console("[%s]\n", readbuf);
|
||
}
|
||
|
||
daemon_send(readbuf, len+DATA_HDRSIZE);
|
||
continue;
|
||
|
||
} else if (STRNEQ(recvbuf, "S390_DUMP_INIT ")) {
|
||
|
||
strcpy(savebuf, recvbuf);
|
||
p1 = strtok(recvbuf, " "); /* S390_DUMP_INIT */
|
||
p2 = strtok(NULL, " "); /* fd */
|
||
p3 = strtok(NULL, " "); /* filename */
|
||
|
||
mfd = atoi(p2);
|
||
for (i = 0; i < MAX_REMOTE_FDS; i++) {
|
||
if (fds[i] == mfd) {
|
||
close(mfd);
|
||
fds[i] = -1;
|
||
break;
|
||
}
|
||
}
|
||
|
||
sprintf(sendbuf, "%s %s", savebuf,
|
||
s390_dump_init(p3) ? "OK" : "<FAIL>");
|
||
|
||
if ((addr = get_s390_panic_task())) {
|
||
sprintf(readbuf, "\npanic_task: %lx\n", addr);
|
||
strcat(sendbuf, readbuf);
|
||
}
|
||
readbuf[0] = NULLCHAR;
|
||
get_s390_panicmsg(readbuf);
|
||
if (strlen(readbuf)) {
|
||
strcat(sendbuf, "panicmsg: ");
|
||
strcat(sendbuf, readbuf);
|
||
}
|
||
|
||
console("[%s]\n", sendbuf);
|
||
daemon_send(sendbuf, strlen(sendbuf));
|
||
continue;
|
||
|
||
} else if (STRNEQ(recvbuf, "S390X_DUMP_INIT ")) {
|
||
|
||
strcpy(savebuf, recvbuf);
|
||
p1 = strtok(recvbuf, " "); /* S390X_DUMP_INIT */
|
||
p2 = strtok(NULL, " "); /* fd */
|
||
p3 = strtok(NULL, " "); /* filename */
|
||
|
||
mfd = atoi(p2);
|
||
for (i = 0; i < MAX_REMOTE_FDS; i++) {
|
||
if (fds[i] == mfd) {
|
||
close(mfd);
|
||
fds[i] = -1;
|
||
break;
|
||
}
|
||
}
|
||
|
||
sprintf(sendbuf, "%s %s", savebuf,
|
||
s390x_dump_init(p3) ? "OK" : "<FAIL>");
|
||
|
||
if ((addr = get_s390x_panic_task())) {
|
||
sprintf(readbuf, "\npanic_task: %lx\n", addr);
|
||
strcat(sendbuf, readbuf);
|
||
}
|
||
readbuf[0] = NULLCHAR;
|
||
get_s390x_panicmsg(readbuf);
|
||
if (strlen(readbuf)) {
|
||
strcat(sendbuf, "panicmsg: ");
|
||
strcat(sendbuf, readbuf);
|
||
}
|
||
|
||
console("[%s]\n", sendbuf);
|
||
daemon_send(sendbuf, strlen(sendbuf));
|
||
continue;
|
||
|
||
} else if (STRNEQ(recvbuf, "READ_S390D ")) {
|
||
|
||
strcpy(savebuf, recvbuf);
|
||
|
||
p1 = strtok(recvbuf, " "); /* READ_S390D */
|
||
p1 = strtok(NULL, " "); /* filename id */
|
||
p2 = strtok(NULL, " "); /* address */
|
||
p3 = strtok(NULL, " "); /* length */
|
||
|
||
mfd = atoi(p1);
|
||
addr = daemon_htol(p2);
|
||
len = atoi(p3);
|
||
|
||
BZERO(readbuf, READBUFSIZE);
|
||
errno = 0;
|
||
|
||
if ((len = read_s390_dumpfile(UNUSED,
|
||
&readbuf[DATA_HDRSIZE], len, UNUSED, addr)) < 0)
|
||
len = 0;
|
||
|
||
if (len) {
|
||
sprintf(readbuf, "%s%07ld", DONEMSG,(ulong)len);
|
||
console("(%ld)\n", (ulong)len);
|
||
} else {
|
||
sprintf(readbuf, "%s%07ld", FAILMSG,
|
||
(ulong)errno);
|
||
console("[%s]\n", readbuf);
|
||
}
|
||
|
||
daemon_send(readbuf, len+DATA_HDRSIZE);
|
||
continue;
|
||
|
||
} else if (STRNEQ(recvbuf, "EXECUTE ")) {
|
||
|
||
strcpy(savebuf, recvbuf);
|
||
p1 = strtok(recvbuf, " "); /* EXECUTE */
|
||
p1 = strtok(NULL, " "); /* bufsize */
|
||
p2 = strtok(NULL, " "); /* MCLXCD or LKCD */
|
||
p3 = strstr(savebuf, p2);
|
||
bufsize = atol(p1);
|
||
|
||
reqsize = bufsize - DATA_HDRSIZE;
|
||
|
||
sprintf(readbuf, "echo | %s", p3);
|
||
|
||
if ((pipe = popen(readbuf, "r")) == NULL) {
|
||
BZERO(readbuf, READBUFSIZE);
|
||
sprintf(readbuf, "%s%07ld", FAILMSG,
|
||
(ulong)errno);
|
||
console("[%s]\n", readbuf);
|
||
daemon_send(readbuf, bufsize);
|
||
continue;
|
||
}
|
||
|
||
errno = cnt = done = total = first = 0;
|
||
|
||
while (!done) {
|
||
BZERO(readbuf, READBUFSIZE);
|
||
|
||
cnt = fread(&readbuf[DATA_HDRSIZE],
|
||
sizeof(char), reqsize, pipe);
|
||
|
||
total += cnt;
|
||
|
||
if (feof(pipe)) {
|
||
sprintf(readbuf, "%s%07ld",
|
||
DONEMSG, (ulong)cnt);
|
||
done = TRUE;
|
||
} else if (ferror(pipe)) {
|
||
sprintf(readbuf, "%s%07ld", FAILMSG,
|
||
(ulong)errno);
|
||
done = TRUE;
|
||
} else
|
||
sprintf(readbuf, "%s%07ld",
|
||
DATAMSG, (ulong)cnt);
|
||
|
||
console("%s[%s]\n", !first++ ? "\n" : "",
|
||
readbuf);
|
||
daemon_send(readbuf, bufsize);
|
||
}
|
||
|
||
console("EXECUTE total: %ld\n", total);
|
||
|
||
pclose(pipe);
|
||
continue;
|
||
|
||
} else if (STRNEQ(recvbuf, "EXIT")) {
|
||
|
||
sprintf(sendbuf, "%s OK", recvbuf);
|
||
console("[%s]\n", sendbuf);
|
||
daemon_send(sendbuf, strlen(sendbuf));
|
||
return;
|
||
|
||
} else {
|
||
sprintf(sendbuf, "%s <FAIL>", recvbuf);
|
||
console("[%s]\n", sendbuf);
|
||
daemon_send(sendbuf, strlen(sendbuf));
|
||
}
|
||
}
|
||
}
|
||
|
||
/*
|
||
* Common error-checking send routine.
|
||
*/
|
||
#define MINSENDSIZE (1448)
|
||
|
||
static void
|
||
daemon_send(void *buffer, int len)
|
||
{
|
||
int remaining, count, ret;
|
||
char *bufptr;
|
||
|
||
remaining = len;
|
||
bufptr = buffer;
|
||
|
||
while (remaining) {
|
||
count = MIN(MINSENDSIZE, remaining);
|
||
|
||
switch (ret = send(rc->sock, bufptr, count, 0))
|
||
{
|
||
case -1:
|
||
switch (errno)
|
||
{
|
||
case ENOBUFS:
|
||
case ENOMEM:
|
||
sleep(1);
|
||
continue;
|
||
default:
|
||
exit(1);
|
||
}
|
||
break;
|
||
|
||
default:
|
||
remaining -= ret;
|
||
bufptr += ret;
|
||
break;
|
||
}
|
||
}
|
||
|
||
console("daemon_send: sent %d\n", len);
|
||
}
|
||
|
||
/*
|
||
* debug print if the -d command line option was used.
|
||
*/
|
||
|
||
int
|
||
console(char *fmt, ...)
|
||
{
|
||
char output[BUFSIZE*2];
|
||
va_list ap;
|
||
int retval;
|
||
FILE *fp;
|
||
|
||
if (!rc->remdebug || !fmt || !strlen(fmt))
|
||
return 0;
|
||
|
||
va_start(ap, fmt);
|
||
(void)vsnprintf(output, BUFSIZE*2, fmt, ap);
|
||
va_end(ap);
|
||
|
||
if ((fp = fopen(rc->remdebugfile, "a")) == NULL)
|
||
return 0;
|
||
|
||
retval = fprintf(fp, "%s", output);
|
||
fclose(fp);
|
||
|
||
return retval;
|
||
}
|
||
|
||
/*
|
||
* Fill in the file size of a freshly opened file.
|
||
*/
|
||
ulong
|
||
daemon_filesize(int fd)
|
||
{
|
||
struct stat sbuf;
|
||
|
||
if (fstat(fd, &sbuf) == 0)
|
||
return(sbuf.st_size);
|
||
else
|
||
return 0;
|
||
}
|
||
|
||
|
||
/*
|
||
* Check for gdb output stating "(no debugging symbols found)".
|
||
*/
|
||
char *
|
||
no_debugging_symbols_found(char *file)
|
||
{
|
||
FILE *pipe;
|
||
char buf[BUFSIZE];
|
||
|
||
sprintf(buf, "echo 'q' | /usr/bin/gdb %s", file);
|
||
if ((pipe = popen(buf, "r")) == NULL)
|
||
return "NO_GDB";
|
||
|
||
while (fgets(buf, BUFSIZE, pipe)) {
|
||
if (strstr(buf, "(no debugging symbols found)")) {
|
||
pclose(pipe);
|
||
return "NO_DEBUG";
|
||
}
|
||
}
|
||
pclose(pipe);
|
||
|
||
return "DEBUG_OK";
|
||
}
|
||
|
||
/*
|
||
* Read /proc/version into a buffer.
|
||
*/
|
||
static int
|
||
daemon_proc_version(char *buf)
|
||
{
|
||
FILE *pipe;
|
||
struct stat sbuf;
|
||
|
||
if (stat("/proc/version", &sbuf) == -1)
|
||
return FALSE;
|
||
|
||
if ((pipe = popen("/bin/cat /proc/version", "r")) == NULL)
|
||
return FALSE;
|
||
|
||
if (fread(buf, sizeof(char),
|
||
BUFSIZE-1, pipe) <= 0) {
|
||
pclose(pipe);
|
||
return FALSE;
|
||
}
|
||
|
||
pclose(pipe);
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
/*
|
||
* c/o W. Richard Stevens...
|
||
*/
|
||
|
||
#define OPEN_MAX_GUESS (256)
|
||
|
||
static int
|
||
daemon_init(void)
|
||
{
|
||
int i;
|
||
pid_t pid;
|
||
int open_max;
|
||
|
||
if ((pid = fork()) < 0)
|
||
return FALSE;
|
||
else if (pid != 0)
|
||
exit(0);
|
||
|
||
setsid();
|
||
chdir("/");
|
||
umask(0);
|
||
|
||
if ((open_max = sysconf(_SC_OPEN_MAX)) < 0)
|
||
open_max = OPEN_MAX_GUESS;
|
||
|
||
for (i = 0; i < open_max; i++)
|
||
close(i);
|
||
|
||
signal(SIGCLD, SIG_IGN);
|
||
|
||
unsetenv("DISPLAY");
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
/*
|
||
* Determine whether a file is in ELF format by checking the magic number
|
||
* in the first EI_NIDENT characters of the file. If it's there, further
|
||
* qualify it by doing a "file" operation on it.
|
||
*/
|
||
static int
|
||
daemon_is_elf_file(char *s)
|
||
{
|
||
int fd, is_elf;
|
||
char magic[EI_NIDENT];
|
||
char buf[BUFSIZE];
|
||
FILE *pipe;
|
||
|
||
if ((fd = open(s, O_RDONLY)) < 0)
|
||
return FALSE;
|
||
|
||
if (read(fd, magic, EI_NIDENT) != EI_NIDENT) {
|
||
close(fd);
|
||
return FALSE;
|
||
}
|
||
close(fd);
|
||
|
||
magic[EI_CLASS] = NULLCHAR;
|
||
|
||
if (!STREQ(magic, ELFMAG))
|
||
return FALSE;
|
||
|
||
sprintf(buf, "/usr/bin/file -L %s", s);
|
||
if ((pipe = popen(buf, "r")) == NULL) {
|
||
console("/usr/bin/strings popen failed\n");
|
||
return TRUE;
|
||
}
|
||
|
||
is_elf = FALSE;
|
||
while (fgets(buf, BUFSIZE-1, pipe)) {
|
||
if (strstr(buf, " ELF ") && strstr(buf, "executable")) {
|
||
is_elf = TRUE;
|
||
break;
|
||
}
|
||
}
|
||
pclose(pipe);
|
||
|
||
return is_elf;
|
||
}
|
||
|
||
/*
|
||
* Translate ASCII hex addresses.
|
||
*/
|
||
static ulong
|
||
daemon_htol(char *s)
|
||
{
|
||
long i, j;
|
||
ulong n;
|
||
|
||
if (strlen(s) > MAX_HEXADDR_STRLEN)
|
||
exit(1);
|
||
|
||
for (n = i = 0; s[i] != 0; i++) {
|
||
switch (s[i])
|
||
{
|
||
case 'a':
|
||
case 'b':
|
||
case 'c':
|
||
case 'd':
|
||
case 'e':
|
||
case 'f':
|
||
j = (s[i] - 'a') + 10;
|
||
break;
|
||
case 'A':
|
||
case 'B':
|
||
case 'C':
|
||
case 'D':
|
||
case 'E':
|
||
case 'F':
|
||
j = (s[i] - 'A') + 10;
|
||
break;
|
||
case '1':
|
||
case '2':
|
||
case '3':
|
||
case '4':
|
||
case '5':
|
||
case '6':
|
||
case '7':
|
||
case '8':
|
||
case '9':
|
||
case '0':
|
||
j = s[i] - '0';
|
||
break;
|
||
case 'x':
|
||
case 'X':
|
||
continue;
|
||
default:
|
||
exit(0);
|
||
}
|
||
n = (16 * n) + j;
|
||
}
|
||
|
||
return(n);
|
||
}
|
||
|
||
|
||
/*
|
||
* Adapted from filesys.c, seach the default directories for a kernel
|
||
* that matches /proc/version. daemon_build_searchdirs() builds an
|
||
* array of directory names.
|
||
*/
|
||
|
||
#define CREATE 1
|
||
#define DESTROY 0
|
||
#define DEFAULT_SEARCHDIRS 4
|
||
|
||
static int
|
||
daemon_find_booted_kernel(char *namelist)
|
||
{
|
||
char kernel[BUFSIZE];
|
||
char command[BUFSIZE];
|
||
char buffer[BUFSIZE];
|
||
char proc_version[BUFSIZE];
|
||
char *version;
|
||
char **searchdirs;
|
||
int i;
|
||
DIR *dirp;
|
||
struct dirent *dp;
|
||
FILE *pipe;
|
||
int found;
|
||
struct stat sbuf;
|
||
|
||
console("\n");
|
||
|
||
if (stat("/proc/version", &sbuf) < 0) {
|
||
console("/proc/version not found\n");
|
||
return FALSE;
|
||
}
|
||
|
||
if (!daemon_proc_version(proc_version)) {
|
||
console("cannot read /proc/version\n");
|
||
return FALSE;
|
||
}
|
||
|
||
version = proc_version;
|
||
|
||
searchdirs = daemon_build_searchdirs(CREATE);
|
||
|
||
for (i = 0, found = FALSE; !found && searchdirs[i]; i++) {
|
||
dirp = opendir(searchdirs[i]);
|
||
if (!dirp)
|
||
continue;
|
||
for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
|
||
sprintf(kernel, "%s%s", searchdirs[i], dp->d_name);
|
||
|
||
if (daemon_mount_point(kernel) ||
|
||
!daemon_file_readable(kernel) ||
|
||
!daemon_is_elf_file(kernel))
|
||
continue;
|
||
|
||
sprintf(command, "/usr/bin/strings %s", kernel);
|
||
if ((pipe = popen(command, "r")) == NULL) {
|
||
console("/usr/bin/strings popen failed\n");
|
||
continue;
|
||
}
|
||
|
||
while (fgets(buffer, BUFSIZE-1, pipe)) {
|
||
if (STREQ(buffer, version)) {
|
||
found = TRUE;
|
||
break;
|
||
}
|
||
}
|
||
pclose(pipe);
|
||
|
||
if (found)
|
||
break;
|
||
}
|
||
closedir(dirp);
|
||
}
|
||
|
||
daemon_mount_point(DESTROY);
|
||
daemon_build_searchdirs(DESTROY);
|
||
|
||
if (found) {
|
||
console("booted kernel: %s\n", kernel);
|
||
strcpy(namelist, kernel);
|
||
return TRUE;
|
||
}
|
||
|
||
console("cannot find booted kernel\n");
|
||
return FALSE;
|
||
}
|
||
|
||
static char **
|
||
daemon_build_searchdirs(int create)
|
||
{
|
||
int i;
|
||
int cnt;
|
||
DIR *dirp;
|
||
struct dirent *dp;
|
||
char dirbuf[BUFSIZE];
|
||
static char **searchdirs = { 0 };
|
||
static char *default_searchdirs[DEFAULT_SEARCHDIRS+1] = {
|
||
"/usr/src/linux/",
|
||
"/boot/",
|
||
"/boot/efi/",
|
||
"/",
|
||
NULL
|
||
};
|
||
|
||
if (!create) {
|
||
if (searchdirs) {
|
||
for (i = DEFAULT_SEARCHDIRS; searchdirs[i]; i++)
|
||
free(searchdirs[i]);
|
||
free(searchdirs);
|
||
}
|
||
return NULL;
|
||
}
|
||
|
||
cnt = DEFAULT_SEARCHDIRS;
|
||
|
||
if ((dirp = opendir("/usr/src"))) {
|
||
for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp))
|
||
cnt++;
|
||
|
||
if ((searchdirs = (char **)malloc(cnt * sizeof(char *)))
|
||
== NULL) {
|
||
console("/usr/src/ directory list malloc failed: %s\n",
|
||
strerror(errno));
|
||
closedir(dirp);
|
||
return default_searchdirs;
|
||
}
|
||
|
||
for (i = 0; i < DEFAULT_SEARCHDIRS; i++)
|
||
searchdirs[i] = default_searchdirs[i];
|
||
cnt = DEFAULT_SEARCHDIRS;
|
||
|
||
rewinddir(dirp);
|
||
|
||
for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
|
||
if (STREQ(dp->d_name, "linux") ||
|
||
STREQ(dp->d_name, ".") ||
|
||
STREQ(dp->d_name, ".."))
|
||
continue;
|
||
|
||
sprintf(dirbuf, "/usr/src/%s", dp->d_name);
|
||
if (daemon_mount_point(dirbuf))
|
||
continue;
|
||
if (!daemon_is_directory(dirbuf))
|
||
continue;
|
||
|
||
if ((searchdirs[cnt] = (char *)
|
||
malloc(strlen(dirbuf)+2)) == NULL) {
|
||
console("/usr/src/ directory entry malloc failed: %s\n",
|
||
strerror(errno));
|
||
break;
|
||
}
|
||
sprintf(searchdirs[cnt], "%s/", dirbuf);
|
||
cnt++;
|
||
}
|
||
searchdirs[cnt] = NULL;
|
||
closedir(dirp);
|
||
}
|
||
|
||
for (i = 0; searchdirs[i]; i++)
|
||
console("searchdirs[%d]: %s\n", i, searchdirs[i]);
|
||
|
||
return searchdirs;
|
||
}
|
||
|
||
/*
|
||
* Determine whether a file is a mount point, without the benefit of stat().
|
||
* This horrendous kludge is necessary to avoid uninterruptible stat() or
|
||
* fstat() calls on nfs mount-points where the remote directory is no longer
|
||
* available.
|
||
*/
|
||
static int
|
||
daemon_mount_point(char *name)
|
||
{
|
||
int i;
|
||
static int mount_points_gathered = -1;
|
||
static char **mount_points;
|
||
char *arglist[MAXARGS];
|
||
char buf[BUFSIZE];
|
||
char cmd[BUFSIZE];
|
||
int argc, found;
|
||
struct stat sbuf;
|
||
FILE *pipe;
|
||
|
||
/*
|
||
* The first time through, stash a list of mount points.
|
||
*/
|
||
|
||
if (mount_points_gathered < 0) {
|
||
found = mount_points_gathered = 0;
|
||
|
||
if (stat("/proc/mounts", &sbuf) == 0)
|
||
sprintf(cmd, "/bin/cat /proc/mounts");
|
||
else if (stat("/etc/mtab", &sbuf) == 0)
|
||
sprintf(cmd, "/bin/cat /etc/mtab");
|
||
else
|
||
return FALSE;
|
||
|
||
if ((pipe = popen(cmd, "r")) == NULL)
|
||
return FALSE;
|
||
|
||
while (fgets(buf, BUFSIZE, pipe)) {
|
||
argc = daemon_parse_line(buf, arglist);
|
||
if (argc < 2)
|
||
continue;
|
||
found++;
|
||
}
|
||
pclose(pipe);
|
||
|
||
if (!(mount_points = (char **)malloc(sizeof(char *) * found)))
|
||
return FALSE;
|
||
|
||
if ((pipe = popen(cmd, "r")) == NULL)
|
||
return FALSE;
|
||
|
||
i = 0;
|
||
while (fgets(buf, BUFSIZE, pipe) &&
|
||
(mount_points_gathered < found)) {
|
||
argc = daemon_parse_line(buf, arglist);
|
||
if (argc < 2)
|
||
continue;
|
||
if ((mount_points[i] = (char *)
|
||
malloc(strlen(arglist[1])*2))) {
|
||
strcpy(mount_points[i], arglist[1]);
|
||
mount_points_gathered++, i++;
|
||
}
|
||
}
|
||
pclose(pipe);
|
||
}
|
||
|
||
/*
|
||
* A null name string means we're done with this routine forever,
|
||
* so the malloc'd memory can be freed.
|
||
*/
|
||
if (!name) {
|
||
for (i = 0; i < mount_points_gathered; i++)
|
||
free(mount_points[i]);
|
||
free(mount_points);
|
||
return FALSE;
|
||
}
|
||
|
||
|
||
for (i = 0; i < mount_points_gathered; i++) {
|
||
if (STREQ(name, mount_points[i]))
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
/*
|
||
* Check whether a file is a directory.
|
||
*/
|
||
static int
|
||
daemon_is_directory(char *file)
|
||
{
|
||
struct stat sbuf;
|
||
|
||
if (!file || !strlen(file))
|
||
return(FALSE);
|
||
|
||
if (stat(file, &sbuf) == -1)
|
||
return(FALSE); /* This file doesn't exist. */
|
||
|
||
return((sbuf.st_mode & S_IFMT) == S_IFDIR ? TRUE : FALSE);
|
||
}
|
||
|
||
/*
|
||
* Check whether a file is readable.
|
||
*/
|
||
static int
|
||
daemon_file_readable(char *file)
|
||
{
|
||
struct stat sbuf;
|
||
long tmp;
|
||
int fd;
|
||
|
||
if (stat(file, &sbuf) < 0)
|
||
return FALSE;
|
||
|
||
if ((fd = open(file, O_RDONLY)) < 0)
|
||
return FALSE;
|
||
|
||
if (read(fd, &tmp, sizeof(tmp)) != sizeof(tmp)) {
|
||
close(fd);
|
||
return FALSE;
|
||
}
|
||
close(fd);
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
/*
|
||
* Parse a line into tokens, populate the passed-in argv[] array, and return
|
||
* the count of arguments found. This function modifies the passed-string
|
||
* by inserting a NULL character at the end of each token. Expressions
|
||
* encompassed by parentheses, and strings encompassed by apostrophes, are
|
||
* collected into single tokens.
|
||
*/
|
||
int
|
||
daemon_parse_line(char *str, char *argv[])
|
||
{
|
||
int i, j;
|
||
int string;
|
||
int expression;
|
||
|
||
for (i = 0; i < MAXARGS; i++)
|
||
argv[i] = NULL;
|
||
|
||
daemon_clean_line(str);
|
||
|
||
if (str == NULL || strlen(str) == 0)
|
||
return(0);
|
||
|
||
i = j = 0;
|
||
string = expression = FALSE;
|
||
argv[j++] = str;
|
||
|
||
while (TRUE) {
|
||
if (j == MAXARGS) {
|
||
console("too many arguments in string!\n");
|
||
return 0;
|
||
}
|
||
|
||
while (str[i] != ' ' && str[i] != '\t' && str[i] != NULLCHAR) {
|
||
i++;
|
||
}
|
||
|
||
switch (str[i])
|
||
{
|
||
case ' ':
|
||
case '\t':
|
||
str[i++] = NULLCHAR;
|
||
|
||
if (str[i] == '"') {
|
||
str[i] = ' ';
|
||
string = TRUE;
|
||
i++;
|
||
}
|
||
|
||
if (str[i] == '(') {
|
||
expression = TRUE;
|
||
}
|
||
|
||
while (str[i] == ' ' || str[i] == '\t') {
|
||
i++;
|
||
}
|
||
|
||
if (str[i] != NULLCHAR && str[i] != '\n') {
|
||
argv[j++] = &str[i];
|
||
if (string) {
|
||
string = FALSE;
|
||
while (str[i] != '"' && str[i] != NULLCHAR)
|
||
i++;
|
||
if (str[i] == '"')
|
||
str[i] = ' ';
|
||
}
|
||
if (expression) {
|
||
expression = FALSE;
|
||
while (str[i] != ')' && str[i] != NULLCHAR)
|
||
i++;
|
||
}
|
||
break;
|
||
}
|
||
/* else fall through */
|
||
case '\n':
|
||
str[i] = NULLCHAR;
|
||
/* keep falling... */
|
||
case NULLCHAR:
|
||
argv[j] = NULLCHAR;
|
||
return(j);
|
||
}
|
||
}
|
||
}
|
||
|
||
/*
|
||
* Strip line-beginning and line-ending whitespace and linefeeds.
|
||
*/
|
||
|
||
char *strip_linefeeds(char *line)
|
||
{
|
||
return(daemon_clean_line(line));
|
||
}
|
||
|
||
static char *
|
||
daemon_clean_line(char *line)
|
||
{
|
||
char buf[BUFSIZE];
|
||
char *p;
|
||
|
||
if (line == NULL || strlen(line) == 0)
|
||
return(line);
|
||
|
||
strcpy(buf, line);
|
||
p = &buf[0];
|
||
while (*p == ' ' || *p == '\t')
|
||
p++;
|
||
strcpy(line, p);
|
||
|
||
if (line == NULL || strlen(line) == 0)
|
||
return(line);
|
||
|
||
p = &LASTCHAR(line);
|
||
|
||
while (*p == '\n')
|
||
*p = NULLCHAR;
|
||
|
||
if (line == NULL || strlen(line) == 0)
|
||
return(line);
|
||
|
||
p = &LASTCHAR(line);
|
||
|
||
while (*p == ' ' || *p == '\t') {
|
||
*p = NULLCHAR;
|
||
if (p == line)
|
||
break;
|
||
p--;
|
||
}
|
||
|
||
return(line);
|
||
}
|
||
|
||
|
||
/*
|
||
* Service not offered by the daemon.
|
||
*/
|
||
|
||
int
|
||
monitor_memory(long *a1, long *a2, long *a3, long *a4)
|
||
{
|
||
return FALSE;
|
||
}
|
||
|
||
static int
|
||
daemon_find_module(char *release, char *filename, char *retbuf)
|
||
{
|
||
char dir[BUFSIZE];
|
||
int found;
|
||
|
||
found = FALSE;
|
||
|
||
sprintf(dir, "%s/%s", DEFAULT_REDHAT_DEBUG_LOCATION, release);
|
||
found = daemon_search_directory_tree(dir, filename, retbuf);
|
||
|
||
if (!found) {
|
||
sprintf(dir, "/lib/modules/%s", release);
|
||
found = daemon_search_directory_tree(dir, filename, retbuf);
|
||
}
|
||
|
||
return found;
|
||
}
|
||
|
||
|
||
int
|
||
daemon_search_directory_tree(char *directory, char *file, char *retbuf)
|
||
{
|
||
char command[BUFSIZE];
|
||
char buf[BUFSIZE];
|
||
FILE *pipe;
|
||
int found;
|
||
|
||
if (!daemon_file_exists("/usr/bin/find", NULL) ||
|
||
!daemon_file_exists("/bin/echo", NULL) ||
|
||
!daemon_is_directory(directory))
|
||
return FALSE;
|
||
|
||
sprintf(command,
|
||
"/usr/bin/find %s -name %s -print; /bin/echo search done",
|
||
directory, file);
|
||
|
||
if ((pipe = popen(command, "r")) == NULL)
|
||
return FALSE;
|
||
|
||
found = FALSE;
|
||
|
||
while (fgets(buf, BUFSIZE-1, pipe) || !found) {
|
||
if (STREQ(buf, "search done\n"))
|
||
break;
|
||
|
||
if (!found &&
|
||
STREQ((char *)basename(strip_linefeeds(buf)), file)) {
|
||
strcpy(retbuf, buf);
|
||
found = TRUE;
|
||
}
|
||
}
|
||
|
||
pclose(pipe);
|
||
|
||
return found;
|
||
}
|
||
|
||
static int
|
||
daemon_file_exists(char *file, struct stat *sp)
|
||
{
|
||
struct stat sbuf;
|
||
|
||
if (stat(file, sp ? sp : &sbuf) == 0)
|
||
return TRUE;
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
|
||
static int
|
||
daemon_checksum(char *file, long *retsum)
|
||
{
|
||
int i;
|
||
int fd;
|
||
ssize_t cnt;
|
||
char buf[MIN_PAGE_SIZE];
|
||
long csum;
|
||
|
||
if ((fd = open(file, O_RDONLY)) < 0)
|
||
return FALSE;
|
||
|
||
csum = 0;
|
||
BZERO(buf, MIN_PAGE_SIZE);
|
||
while ((cnt = read(fd, buf, MIN_PAGE_SIZE)) > 0) {
|
||
for (i = 0; i < cnt; i++)
|
||
csum += buf[i];
|
||
BZERO(buf, MIN_PAGE_SIZE);
|
||
}
|
||
close(fd);
|
||
|
||
*retsum = csum;
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
#else
|
||
|
||
static void copy_to_local_namelist(struct remote_file *);
|
||
static char *create_local_namelist(struct remote_file *);
|
||
static int remote_find_booted_kernel(struct remote_file *);
|
||
static int remote_proc_version(char *);
|
||
static int validate_phys_base(physaddr_t, physaddr_t, physaddr_t);
|
||
static int remote_file_open(struct remote_file *);
|
||
static int remote_file_close(struct remote_file *);
|
||
static int identical_namelist(char *, struct remote_file *);
|
||
void remote_socket_options(int);
|
||
static int copy_remote_file(struct remote_file *, int, char *, char *);
|
||
static void copy_remote_gzip_file(struct remote_file *, char *, char *);
|
||
static int remote_file_checksum(struct remote_file *);
|
||
static int remote_file_type(char *);
|
||
static int remote_lkcd_dump_init(void);
|
||
static int remote_s390_dump_init(void);
|
||
static int remote_netdump_init(void);
|
||
static int remote_tcp_read(int, const char *, size_t);
|
||
static int remote_tcp_read_string(int, const char *, size_t, int);
|
||
static int remote_tcp_write(int, const void *, size_t);
|
||
static int remote_tcp_write_string(int, const char *);
|
||
|
||
struct _remote_context {
|
||
uint flags;
|
||
int n_cpus;
|
||
int vfd;
|
||
char remote_type[10];
|
||
} remote_context;
|
||
|
||
#define NIL_FLAG (0x01U)
|
||
|
||
#define NIL_MODE() (rc->flags & NIL_FLAG)
|
||
|
||
struct _remote_context *rc = &remote_context;
|
||
|
||
/*
|
||
* Parse, verify and establish a connection with the network daemon
|
||
* specified on the crash command line.
|
||
*
|
||
* The format is: [remote-hostname]:port[,remote-namelist][,remote-dumpfile]
|
||
*
|
||
* where everything but the port number is optional, and the remote-namelist
|
||
* and remote-dumpfile can be reversed.
|
||
*
|
||
* 1. The default remote host is the local host.
|
||
* 2. The default dumpfile is /dev/mem.
|
||
* 3. If no remote-namelist and remote-dumpfile are given, the daemon
|
||
* is queried for a kernel that matches the remote /proc/version.
|
||
* If no local kernel namelist is entered, the remote version will
|
||
* be copied locally when fd_init() is called.
|
||
* 4. If a remote-dumpfile is given with no remote namelist, it is presumed
|
||
* that the kernel namelist will be entered locally.
|
||
*/
|
||
|
||
int
|
||
is_remote_daemon(char *dp)
|
||
{
|
||
char *p1;
|
||
static char defaulthost[MAXHOSTNAMELEN+1];
|
||
char sendbuf[BUFSIZE];
|
||
char recvbuf[BUFSIZE];
|
||
char *portp, *filep, *file1, *file2;
|
||
struct hostent *hp;
|
||
struct sockaddr_in serv_addr;
|
||
|
||
if (!strstr(dp, ":") || file_exists(dp, NULL))
|
||
return FALSE;
|
||
|
||
pc->port = 0;
|
||
pc->server = pc->server_memsrc = NULL;
|
||
rc->vfd = pc->rmfd = pc->rkfd = -1;
|
||
file1 = file2 = NULL;
|
||
|
||
if ((filep = strstr(dp, ","))) {
|
||
*filep = NULLCHAR;
|
||
filep++;
|
||
}
|
||
|
||
if (*dp == ':') {
|
||
BZERO(defaulthost, MAXHOSTNAMELEN+1);
|
||
gethostname(defaulthost, MAXHOSTNAMELEN);
|
||
pc->server = defaulthost;
|
||
portp = dp+1;
|
||
} else {
|
||
pc->server = strtok(dp, ":");
|
||
portp = strtok(NULL, ":");
|
||
}
|
||
|
||
if (portp == NULL)
|
||
return FALSE;
|
||
|
||
if (decimal(portp, 0))
|
||
pc->port = (ushort)atoi(portp);
|
||
else
|
||
return FALSE;
|
||
|
||
if (filep) {
|
||
file1 = strtok(filep, ",");
|
||
file2 = strtok(NULL, ",");
|
||
}
|
||
|
||
if (!pc->server || !pc->port)
|
||
return FALSE;
|
||
|
||
if (CRASHDEBUG(1)) {
|
||
fprintf(fp, "server: [%s]\n", pc->server);
|
||
fprintf(fp, " port: [%d]\n", pc->port);
|
||
fprintf(fp, " file1: [%s]\n", file1);
|
||
fprintf(fp, " file2: [%s]\n", file2);
|
||
}
|
||
|
||
if ((hp = gethostbyname(pc->server)) == NULL) {
|
||
herror(pc->server);
|
||
error(FATAL, "gethostbyname [%s] failed\n", pc->server);
|
||
}
|
||
|
||
if (CRASHDEBUG(1)) {
|
||
struct in_addr *ip;
|
||
char **listptr;
|
||
|
||
listptr = hp->h_addr_list;
|
||
while ((ip = (struct in_addr *) *listptr++) != NULL)
|
||
printf("%s\n", inet_ntoa(*ip));
|
||
}
|
||
|
||
if ((pc->sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
|
||
perror("socket");
|
||
error(FATAL, "socket call failed\n");
|
||
}
|
||
|
||
BZERO((char *)&serv_addr, sizeof(struct sockaddr_in));
|
||
serv_addr.sin_family = AF_INET;
|
||
BCOPY(hp->h_addr, (char *)&serv_addr.sin_addr, hp->h_length);
|
||
serv_addr.sin_port = htons(pc->port);
|
||
|
||
if (connect(pc->sockfd, (struct sockaddr *)&serv_addr,
|
||
sizeof(struct sockaddr_in)) < 0) {
|
||
herror(hp->h_name);
|
||
error(FATAL, "connect [%s:%d] failed\n", hp->h_name, pc->port);
|
||
clean_exit(1);
|
||
}
|
||
|
||
if (CRASHDEBUG(1))
|
||
printf("connect [%s:%d]: success\n", hp->h_name, pc->port);
|
||
|
||
remote_socket_options(pc->sockfd);
|
||
|
||
/*
|
||
* Try and use NIL mode.
|
||
*/
|
||
BZERO(sendbuf, BUFSIZE);
|
||
BZERO(recvbuf, BUFSIZE);
|
||
sprintf(sendbuf, "NIL");
|
||
remote_tcp_write_string(pc->sockfd, sendbuf);
|
||
remote_tcp_read_string(pc->sockfd, recvbuf, BUFSIZE-1, 0);
|
||
if (!strstr(recvbuf, "<FAIL>")) {
|
||
rc->flags |= NIL_FLAG;
|
||
p1 = strtok(recvbuf, " "); /* NIL */
|
||
p1 = strtok(NULL, " "); /* remote type */
|
||
if (p1 && p1[0] != 'L')
|
||
pc->flags2 |= REM_PAUSED_F;
|
||
}
|
||
/*
|
||
* Get the remote machine type and verify a match. The daemon pid
|
||
* is also used as a live system initial context.
|
||
*/
|
||
BZERO(sendbuf, BUFSIZE);
|
||
BZERO(recvbuf, BUFSIZE);
|
||
sprintf(sendbuf, "MACHINE_PID");
|
||
remote_tcp_write_string(pc->sockfd, sendbuf);
|
||
remote_tcp_read_string(pc->sockfd, recvbuf, BUFSIZE-1, NIL_MODE());
|
||
p1 = strtok(recvbuf, " "); /* MACHINE */
|
||
p1 = strtok(NULL, " "); /* machine type */
|
||
if (CRASHDEBUG(1))
|
||
printf("remote MACHINE: %s\n", p1);
|
||
if (!STREQ(pc->machine_type, p1))
|
||
error(FATAL, "machine type mismatch: local: %s remote: %s\n",
|
||
pc->machine_type, p1);
|
||
p1 = strtok(NULL, " "); /* pid */
|
||
pc->server_pid = atol(p1);
|
||
|
||
if (file1) {
|
||
switch (remote_file_type(file1))
|
||
{
|
||
case TYPE_ELF:
|
||
pc->server_namelist = file1;
|
||
break;
|
||
case TYPE_NETDUMP:
|
||
pc->server_memsrc = file1;
|
||
pc->flags |= REM_NETDUMP;
|
||
break;
|
||
case TYPE_MCLXCD:
|
||
pc->server_memsrc = file1;
|
||
pc->flags |= REM_MCLXCD;
|
||
break;
|
||
case TYPE_DEVMEM:
|
||
pc->server_memsrc = file1;
|
||
break;
|
||
case TYPE_LKCD:
|
||
pc->server_memsrc = file1;
|
||
pc->flags |= REM_LKCD;
|
||
break;
|
||
case TYPE_S390D:
|
||
pc->server_memsrc = file1;
|
||
pc->flags |= REM_S390D;
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (file2) {
|
||
switch (remote_file_type(file2))
|
||
{
|
||
case TYPE_ELF:
|
||
if (pc->server_namelist)
|
||
error(FATAL,
|
||
"two remote namelists entered: %s and %s\n",
|
||
file1, file2);
|
||
pc->server_namelist = file2;
|
||
break;
|
||
case TYPE_NETDUMP:
|
||
if (pc->server_memsrc)
|
||
error(FATAL,
|
||
"neither %s or %s is an ELF file\n",
|
||
file1, file2);
|
||
pc->server_memsrc = file2;
|
||
pc->flags |= REM_NETDUMP;
|
||
break;
|
||
case TYPE_MCLXCD:
|
||
if (pc->server_memsrc)
|
||
error(FATAL,
|
||
"neither %s or %s is an ELF file\n",
|
||
file1, file2);
|
||
pc->server_memsrc = file2;
|
||
pc->flags |= REM_MCLXCD;
|
||
break;
|
||
case TYPE_LKCD:
|
||
if (pc->server_memsrc)
|
||
error(FATAL,
|
||
"neither %s or %s is an ELF file\n",
|
||
file1, file2);
|
||
pc->server_memsrc = file2;
|
||
pc->flags |= REM_LKCD;
|
||
break;
|
||
case TYPE_S390D:
|
||
if (pc->server_memsrc)
|
||
error(FATAL,
|
||
"neither %s or %s is an ELF file\n",
|
||
file1, file2);
|
||
pc->server_memsrc = file2;
|
||
pc->flags |= REM_S390D;
|
||
break;
|
||
case TYPE_DEVMEM:
|
||
pc->server_memsrc = file2;
|
||
break;
|
||
}
|
||
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
/*
|
||
* Determine whether a file is a kernel or a memory source.
|
||
*/
|
||
static int
|
||
remote_file_type(char *file)
|
||
{
|
||
char sendbuf[BUFSIZE];
|
||
char recvbuf[BUFSIZE];
|
||
|
||
BZERO(sendbuf, BUFSIZE);
|
||
BZERO(recvbuf, BUFSIZE);
|
||
sprintf(sendbuf, "TYPE %s", file);
|
||
remote_tcp_write_string(pc->sockfd, sendbuf);
|
||
remote_tcp_read_string(pc->sockfd, recvbuf, BUFSIZE-1, NIL_MODE());
|
||
|
||
if (strstr(recvbuf, "<FAIL>"))
|
||
error(FATAL, "invalid remote file name: %s\n", file);
|
||
else if (strstr(recvbuf, " UNSUPPORTED"))
|
||
error(FATAL, "unsupported remote file type: %s\n", file);
|
||
else if (strstr(recvbuf, " NETDUMP"))
|
||
return TYPE_NETDUMP;
|
||
else if (strstr(recvbuf, " ELF"))
|
||
return TYPE_ELF;
|
||
else if (strstr(recvbuf, " MCLXCD"))
|
||
return TYPE_MCLXCD;
|
||
else if (strstr(recvbuf, " DEVMEM"))
|
||
return TYPE_DEVMEM;
|
||
else if (strstr(recvbuf, " LKCD"))
|
||
return TYPE_LKCD;
|
||
else if (strstr(recvbuf, " S390D"))
|
||
return TYPE_S390D;
|
||
|
||
return (error(FATAL, "unknown remote file type: %s\n", file));
|
||
}
|
||
|
||
/*
|
||
* Try to set the receive buffer size to READBUFSIZE with setsockopt(),
|
||
* storing the value returned by getsockopt() after the attempt is made.
|
||
* Then enforce a SO_RCVLOWAT (low water mark) of 1, to ensure that error
|
||
* recovery won't get hung in the recv() call in remote_clear_pipeline().
|
||
*/
|
||
void
|
||
remote_socket_options(int sockfd)
|
||
{
|
||
int rcvbuf, optlen;
|
||
|
||
pc->rcvbufsize = rcvbuf = READBUFSIZE;
|
||
|
||
if (setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, (char *)&rcvbuf,
|
||
sizeof(rcvbuf)) < 0) {
|
||
error(INFO, "SO_RCVBUF setsockopt error\n");
|
||
return;
|
||
}
|
||
|
||
optlen = sizeof(rcvbuf);
|
||
if (getsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, (char *)&rcvbuf,
|
||
(socklen_t *)&optlen) < 0) {
|
||
error(INFO, "SO_RCVBUF getsockopt error\n");
|
||
return;
|
||
}
|
||
|
||
if (CRASHDEBUG(1))
|
||
printf("socket SO_RCVBUF size: %d\n", rcvbuf);
|
||
|
||
rcvbuf = 1;
|
||
|
||
if (setsockopt(sockfd, SOL_SOCKET, SO_RCVLOWAT, (char *)&rcvbuf,
|
||
sizeof(rcvbuf)) < 0) {
|
||
/*
|
||
* Earlier versions of Linux TCP won't accept this option,
|
||
* which is hardcoded to the desired count of 1 anyway.
|
||
* Set it to 0, and verify it as 1 in the getsockopt() call.
|
||
*/
|
||
if (CRASHDEBUG(1))
|
||
error(INFO, "SO_RCVLOWAT setsockopt error: %s\n",
|
||
strerror(errno));
|
||
rcvbuf = 0;
|
||
}
|
||
|
||
optlen = sizeof(rcvbuf);
|
||
if (getsockopt(sockfd, SOL_SOCKET, SO_RCVLOWAT, (char *)&rcvbuf,
|
||
(socklen_t *)&optlen) < 0) {
|
||
error(INFO, "SO_RCVLOWAT getsockopt error\n");
|
||
return;
|
||
}
|
||
|
||
if (CRASHDEBUG(1) || (rcvbuf != 1))
|
||
error(INFO, "socket SO_RCVLOWAT value: %d\n", rcvbuf);
|
||
|
||
}
|
||
|
||
/*
|
||
* Wrapper around recv to read full length packet.
|
||
*/
|
||
static int
|
||
remote_tcp_read(int sock, const char *pv_buffer, size_t cb_buffer)
|
||
{
|
||
size_t cb_total = 0;
|
||
|
||
do
|
||
{
|
||
ssize_t cb_read = recv(sock, (void*)pv_buffer, cb_buffer, MSG_NOSIGNAL);
|
||
|
||
if (cb_read <= 0)
|
||
return cb_read;
|
||
cb_total += cb_read;
|
||
cb_buffer -= cb_read;
|
||
pv_buffer = (char *)pv_buffer + cb_read;
|
||
} while (cb_buffer);
|
||
|
||
return cb_total;
|
||
}
|
||
|
||
/*
|
||
* Wrapper around recv to read full string packet.
|
||
*/
|
||
static int
|
||
remote_tcp_read_string(int sock, const char *pv_buffer, size_t cb_buffer, int nil_mode)
|
||
{
|
||
size_t cb_total = 0;
|
||
|
||
do
|
||
{
|
||
ssize_t cb_read = recv(sock, (void*)pv_buffer, cb_buffer, MSG_NOSIGNAL);
|
||
|
||
if (cb_read <= 0)
|
||
return cb_read;
|
||
cb_total += cb_read;
|
||
if (!nil_mode && cb_total >= 4)
|
||
return cb_total;
|
||
if (!pv_buffer[cb_read - 1])
|
||
return cb_total;
|
||
cb_buffer -= cb_read;
|
||
pv_buffer = (char *)pv_buffer + cb_read;
|
||
} while (cb_buffer);
|
||
|
||
return cb_total;
|
||
}
|
||
|
||
/*
|
||
* Wrapper around send to send full packet.
|
||
*/
|
||
static int
|
||
remote_tcp_write(int sock, const void *pv_buffer, size_t cb_buffer)
|
||
{
|
||
do
|
||
{
|
||
size_t cb_now = cb_buffer;
|
||
ssize_t cb_written = send(sock, (const char *)pv_buffer, cb_now, MSG_NOSIGNAL);
|
||
|
||
if (cb_written < 0)
|
||
return 1;
|
||
cb_buffer -= cb_written;
|
||
pv_buffer = (char *)pv_buffer + cb_written;
|
||
} while (cb_buffer);
|
||
|
||
return 0;
|
||
}
|
||
|
||
/*
|
||
* Wrapper around tcp_write to send a string
|
||
*/
|
||
static int
|
||
remote_tcp_write_string(int sock, const char *pv_buffer)
|
||
{
|
||
return remote_tcp_write(sock, pv_buffer, strlen(pv_buffer) + 1);
|
||
}
|
||
|
||
|
||
/*
|
||
* Request that the daemon open a file.
|
||
*/
|
||
static int
|
||
remote_file_open(struct remote_file *rfp)
|
||
{
|
||
char sendbuf[BUFSIZE];
|
||
char recvbuf[BUFSIZE];
|
||
char *p1;
|
||
|
||
BZERO(sendbuf, BUFSIZE);
|
||
BZERO(recvbuf, BUFSIZE);
|
||
sprintf(sendbuf, "OPEN %s", rfp->filename);
|
||
remote_tcp_write_string(pc->sockfd, sendbuf);
|
||
remote_tcp_read_string(pc->sockfd, recvbuf, BUFSIZE-1, NIL_MODE());
|
||
|
||
if (CRASHDEBUG(1))
|
||
fprintf(fp, "remote_file_open: [%s]\n", recvbuf);
|
||
|
||
if (strstr(recvbuf, "O_RDWR") || strstr(recvbuf, "O_RDONLY")) {
|
||
p1 = strtok(recvbuf, " "); /* OPEN */
|
||
p1 = strtok(NULL, " "); /* filename */
|
||
p1 = strtok(NULL, " "); /* fd */
|
||
rfp->fd = atoi(p1);
|
||
p1 = strtok(NULL, " "); /* flags */
|
||
if (STREQ(p1, "O_RDWR"))
|
||
rfp->flags |= O_RDWR;
|
||
else if (STREQ(p1, "O_RDONLY"))
|
||
rfp->flags |= O_RDONLY;
|
||
p1 = strtok(NULL, " "); /* size */
|
||
rfp->size = atoi(p1);
|
||
return TRUE;
|
||
} else
|
||
return FALSE;
|
||
}
|
||
|
||
/*
|
||
* Request that the daemon close a previously-opened file.
|
||
*/
|
||
static int
|
||
remote_file_close(struct remote_file *rfp)
|
||
{
|
||
char sendbuf[BUFSIZE];
|
||
char recvbuf[BUFSIZE];
|
||
|
||
BZERO(sendbuf, BUFSIZE);
|
||
BZERO(recvbuf, BUFSIZE);
|
||
sprintf(sendbuf, "CLOSE %d", rfp->fd);
|
||
remote_tcp_write_string(pc->sockfd, sendbuf);
|
||
remote_tcp_read_string(pc->sockfd, recvbuf, BUFSIZE-1, NIL_MODE());
|
||
|
||
return (strstr(recvbuf, "OK") ? TRUE : FALSE);
|
||
}
|
||
|
||
/*
|
||
* Get a copy of the daemon machine's /proc/version
|
||
*/
|
||
static int
|
||
remote_proc_version(char *buf)
|
||
{
|
||
char sendbuf[BUFSIZE];
|
||
char recvbuf[BUFSIZE];
|
||
|
||
BZERO(sendbuf, BUFSIZE);
|
||
BZERO(recvbuf, BUFSIZE);
|
||
sprintf(sendbuf, "PROC_VERSION");
|
||
remote_tcp_write_string(pc->sockfd, sendbuf);
|
||
remote_tcp_read_string(pc->sockfd, recvbuf, BUFSIZE-1, NIL_MODE());
|
||
if (STREQ(recvbuf, "<FAIL>")) {
|
||
buf[0] = 0;
|
||
return FALSE;
|
||
}
|
||
strcpy(buf, recvbuf);
|
||
return TRUE;
|
||
}
|
||
|
||
/*
|
||
* Check that virt_phys_base when accessed via
|
||
* phys_base - text_start is phys_base.
|
||
*/
|
||
static int
|
||
validate_phys_base(physaddr_t phys_base, physaddr_t text_start, physaddr_t virt_phys_base)
|
||
{
|
||
ulong value;
|
||
|
||
if (CRASHDEBUG(3))
|
||
fprintf(fp, "validate_phys_base: virt_phys_base=0x%llx phys_base=0x%llx text_start=0x%llx calc=0x%llx\n",
|
||
(long long unsigned int)virt_phys_base,
|
||
(long long unsigned int)phys_base,
|
||
(long long unsigned int)text_start,
|
||
(long long unsigned int)virt_phys_base + phys_base - text_start);
|
||
|
||
if (READMEM(pc->rmfd, (void*)&value, sizeof(value),
|
||
virt_phys_base, virt_phys_base + phys_base - text_start)
|
||
== sizeof(value)) {
|
||
if (value == phys_base)
|
||
return 1;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
/*
|
||
* Get remote phys_base based on virtual address of "phys_base".
|
||
*/
|
||
physaddr_t
|
||
get_remote_phys_base(physaddr_t text_start, physaddr_t virt_phys_base)
|
||
{
|
||
int vcpu;
|
||
ulong value;
|
||
|
||
if (rc->vfd < 0) {
|
||
struct remote_file remote_file, *rfp;
|
||
|
||
rfp = &remote_file;
|
||
BZERO(rfp, sizeof(struct remote_file));
|
||
rfp->filename = "/dev/vmem";
|
||
if (remote_file_open(rfp)) {
|
||
rc->vfd = rfp->fd;
|
||
} else
|
||
return 0;
|
||
}
|
||
|
||
for (vcpu = 0; vcpu < rc->n_cpus; vcpu++)
|
||
if (remote_memory_read(rc->vfd, (void*)&value, sizeof(value),
|
||
virt_phys_base, vcpu) == sizeof(value)) {
|
||
if (validate_phys_base(value, text_start, virt_phys_base))
|
||
return value;
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
/*
|
||
* Do a remote VTOP if supported.
|
||
*/
|
||
physaddr_t
|
||
remote_vtop(int cpu, physaddr_t virt_addr)
|
||
{
|
||
char sendbuf[BUFSIZE];
|
||
char recvbuf[BUFSIZE];
|
||
char *p1;
|
||
int errflag;
|
||
ulong value;
|
||
|
||
if (!rc->remote_type[0])
|
||
return 0; /* Not a special remote. */
|
||
|
||
BZERO(sendbuf, BUFSIZE);
|
||
BZERO(recvbuf, BUFSIZE);
|
||
sprintf(sendbuf, "VTOP %d %llx", cpu, (long long unsigned int)virt_addr);
|
||
remote_tcp_write_string(pc->sockfd, sendbuf);
|
||
remote_tcp_read_string(pc->sockfd, recvbuf, BUFSIZE-1, NIL_MODE());
|
||
|
||
if (CRASHDEBUG(2))
|
||
fprintf(fp, "remote_vtop: [%s]\n", recvbuf);
|
||
|
||
if (strstr(recvbuf, "<FAIL>"))
|
||
error(FATAL, "remote_vtop for CPU %d\n", cpu);
|
||
p1 = strtok(recvbuf, " "); /* VTOP */
|
||
p1 = strtok(NULL, " "); /* cpu */
|
||
p1 = strtok(NULL, " "); /* vaddr */
|
||
p1 = strtok(NULL, " "); /* paddr */
|
||
|
||
errflag = 0;
|
||
value = htol(p1, RETURN_ON_ERROR|QUIET, &errflag);
|
||
if (!errflag) {
|
||
return value;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
/*
|
||
* Get a copy of the daemon machine cpu regs.
|
||
*/
|
||
int
|
||
get_remote_regs(struct bt_info *bt, ulong *eip, ulong *esp)
|
||
{
|
||
char sendbuf[BUFSIZE];
|
||
char recvbuf[BUFSIZE];
|
||
char *p1, *p2;
|
||
int errflag;
|
||
ulong value;
|
||
|
||
if (!rc->remote_type[0])
|
||
return 0; /* Not a special remote. */
|
||
|
||
*eip = 0;
|
||
*esp = 0;
|
||
|
||
BZERO(sendbuf, BUFSIZE);
|
||
BZERO(recvbuf, BUFSIZE);
|
||
sprintf(sendbuf, "FETCH_LIVE_IP_SP_BP %d", bt->tc->processor);
|
||
if (remote_tcp_write_string(pc->sockfd, sendbuf))
|
||
return 0;
|
||
errflag = remote_tcp_read_string(pc->sockfd, recvbuf, BUFSIZE-1, NIL_MODE());
|
||
if (errflag <= 0)
|
||
return 0;
|
||
|
||
if (CRASHDEBUG(1))
|
||
fprintf(fp, "get_remote_regs(cpu=%d): [%s]\n",
|
||
bt->tc->processor, recvbuf);
|
||
|
||
if (strstr(recvbuf, "<FAIL>")) {
|
||
error(INFO, "get_remote_regs for CPU %d\n", bt->tc->processor);
|
||
return 0;
|
||
}
|
||
p1 = strtok(recvbuf, " "); /* FETCH_LIVE_IP_SP_BP */
|
||
p1 = strtok(NULL, " "); /* cpu */
|
||
p1 = strtok(NULL, ":"); /* cs */
|
||
p1 = strtok(NULL, " "); /* ip */
|
||
p2 = strtok(NULL, ":"); /* ss */
|
||
p2 = strtok(NULL, " "); /* sp */
|
||
/* p2 = strtok(NULL, " "); bp */
|
||
|
||
errflag = 0;
|
||
value = htol(p1, RETURN_ON_ERROR|QUIET, &errflag);
|
||
if (!errflag) {
|
||
*eip = value;
|
||
}
|
||
|
||
errflag = 0;
|
||
value = htol(p2, RETURN_ON_ERROR|QUIET, &errflag);
|
||
if (!errflag) {
|
||
*esp = value;
|
||
}
|
||
return 1;
|
||
}
|
||
|
||
/*
|
||
* Get a remote cr3 if supported.
|
||
*/
|
||
physaddr_t
|
||
get_remote_cr3(int cpu)
|
||
{
|
||
char sendbuf[BUFSIZE];
|
||
char recvbuf[BUFSIZE];
|
||
char *p1;
|
||
int errflag;
|
||
ulong value;
|
||
|
||
if (!rc->remote_type[0])
|
||
return 0; /* Not a special remote. */
|
||
|
||
BZERO(sendbuf, BUFSIZE);
|
||
BZERO(recvbuf, BUFSIZE);
|
||
sprintf(sendbuf, "FETCH_LIVE_CR3 %d", cpu);
|
||
if (remote_tcp_write_string(pc->sockfd, sendbuf))
|
||
return 0;
|
||
remote_tcp_read_string(pc->sockfd, recvbuf, BUFSIZE-1, NIL_MODE());
|
||
|
||
if (CRASHDEBUG(1))
|
||
fprintf(fp, "get_remote_cr3: [%s]\n", recvbuf);
|
||
|
||
if (strstr(recvbuf, "<FAIL>"))
|
||
error(FATAL, "get_remote_cr3 for CPU %d\n", cpu);
|
||
p1 = strtok(recvbuf, " "); /* FETCH_LIVE_CR3 */
|
||
p1 = strtok(NULL, " "); /* cpu */
|
||
p1 = strtok(NULL, " "); /* cr3 */
|
||
|
||
errflag = 0;
|
||
value = htol(p1, RETURN_ON_ERROR|QUIET, &errflag);
|
||
if (!errflag)
|
||
return value;
|
||
return 0;
|
||
}
|
||
|
||
|
||
/*
|
||
*
|
||
* Set up the file descriptors and file name strings if they haven't
|
||
* been set up before:
|
||
*
|
||
* 1. pc->namelist must be set to a local kernel namelist, which will be
|
||
* copied from the remote machine if it was not specified.
|
||
*
|
||
* 2. pc->dumpfile will never be set for a remote operation, because there
|
||
* is no difference to readmem().
|
||
*
|
||
* 3. pc->server_namelist may be set if it has to be copied across.
|
||
*
|
||
* 4. pc->server_memsrc will be set to either /dev/mem or the dumpfile.
|
||
*/
|
||
void
|
||
remote_fd_init(void)
|
||
{
|
||
char filename[BUFSIZE];
|
||
struct remote_file remote_file, *rfp;
|
||
|
||
rfp = &remote_file;
|
||
|
||
if (pc->namelist && pc->server_namelist) {
|
||
error(INFO, "too many namelists\n");
|
||
program_usage(SHORT_FORM);
|
||
}
|
||
|
||
if ((pc->namelist || pc->server_namelist) &&
|
||
pc->namelist_debug && pc->system_map) {
|
||
error(INFO,
|
||
"too many namelist options:\n %s\n %s\n %s\n",
|
||
pc->namelist ?
|
||
pc->namelist : pc->server_namelist,
|
||
pc->namelist_debug,
|
||
pc->system_map);
|
||
program_usage(SHORT_FORM);
|
||
}
|
||
|
||
/*
|
||
* Account for the remote possibility of a local dumpfile
|
||
* being entered on the command line.
|
||
*/
|
||
if (pc->flags & MEMORY_SOURCES) {
|
||
if (pc->server_memsrc) {
|
||
error(INFO, "too many dumpfile/memory arguments\n");
|
||
program_usage(SHORT_FORM);
|
||
}
|
||
pc->flags2 |= MEMSRC_LOCAL;
|
||
if (pc->flags & (DEVMEM|MEMMOD)) {
|
||
if (!get_proc_version())
|
||
error(INFO, "/proc/version: %s\n",
|
||
strerror(errno));
|
||
pc->flags |= LIVE_SYSTEM;
|
||
}
|
||
} else {
|
||
/*
|
||
* First open the remote memory source, defaulting to /dev/mem
|
||
* if no remote dumpfile name was entered. If it is /dev/mem,
|
||
* then also go get the remote /proc/version.
|
||
*/
|
||
pc->readmem = read_daemon;
|
||
|
||
if (!pc->server_memsrc)
|
||
pc->server_memsrc = "/dev/mem";
|
||
|
||
if (STREQ(pc->server_memsrc, "/dev/mem"))
|
||
pc->flags |= REM_LIVE_SYSTEM;
|
||
|
||
BZERO(rfp, sizeof(struct remote_file));
|
||
rfp->filename = pc->server_memsrc;
|
||
|
||
if (remote_file_open(rfp)) {
|
||
pc->rmfd = rfp->fd;
|
||
if (rfp->flags & O_RDWR)
|
||
pc->flags |= MFD_RDWR;
|
||
|
||
if (BITS32() && REMOTE_ACTIVE()) {
|
||
BZERO(rfp, sizeof(struct remote_file));
|
||
rfp->filename = "/dev/kmem";
|
||
if (remote_file_open(rfp))
|
||
pc->rkfd = rfp->fd;
|
||
}
|
||
|
||
if ((pc->flags & REM_NETDUMP) &&
|
||
!remote_netdump_init())
|
||
error(FATAL,
|
||
"%s: remote initialization failed\n",
|
||
pc->server_memsrc);
|
||
|
||
if ((pc->flags & REM_LKCD) &&
|
||
!remote_lkcd_dump_init())
|
||
error(FATAL,
|
||
"%s: remote initialization failed\n",
|
||
pc->server_memsrc);
|
||
|
||
if ((pc->flags & REM_S390D) &&
|
||
!remote_s390_dump_init())
|
||
error(FATAL,
|
||
"%s: remote initialization failed\n",
|
||
pc->server_memsrc);
|
||
|
||
if (REMOTE_DUMPFILE())
|
||
pc->writemem = write_daemon;
|
||
|
||
} else
|
||
error(FATAL, "cannot open remote memory source: %s\n",
|
||
pc->server_memsrc);
|
||
|
||
if (REMOTE_ACTIVE() && !remote_proc_version(kt->proc_version))
|
||
error(WARNING,
|
||
"daemon cannot access /proc/version\n\n");
|
||
}
|
||
|
||
/*
|
||
* If a local namelist was entered, check whether it's readable.
|
||
* If a server namelist was entered, copy it across.
|
||
* If no server namelist was entered, query the daemon for it,
|
||
* and if found, copy it across,
|
||
*/
|
||
if (pc->namelist) {
|
||
if ((pc->nfd = open(pc->namelist, O_RDONLY)) < 0)
|
||
error(FATAL, "%s: %s\n", pc->namelist, strerror(errno));
|
||
close(pc->nfd);
|
||
pc->nfd = -1;
|
||
pc->flags |= NAMELIST_LOCAL;
|
||
} else if (pc->server_namelist) {
|
||
BZERO(rfp, sizeof(struct remote_file));
|
||
rfp->filename = pc->server_namelist;
|
||
if (!remote_file_open(rfp)) {
|
||
error(FATAL, "daemon cannot open: %s\n",
|
||
pc->server_namelist);
|
||
}
|
||
copy_to_local_namelist(rfp);
|
||
remote_file_close(rfp);
|
||
} else {
|
||
BZERO(rfp, sizeof(struct remote_file));
|
||
BZERO(filename, BUFSIZE);
|
||
rfp->filename = filename;
|
||
if (!remote_find_booted_kernel(rfp))
|
||
error(FATAL,
|
||
"remote daemon cannot find booted kernel\n");
|
||
if (!remote_file_open(rfp))
|
||
error(FATAL, "remote daemon cannot open: %s\n",
|
||
pc->server_namelist);
|
||
copy_to_local_namelist(rfp);
|
||
remote_file_close(rfp);
|
||
}
|
||
|
||
if (REMOTE_ACTIVE())
|
||
pc->flags |= LIVE_SYSTEM;
|
||
}
|
||
|
||
/*
|
||
* Copy a remote kernel to a local file, which gets unlinked in the normal
|
||
* course of events. However, the pc->nfd file descriptor will be kept
|
||
* alive in case there's a command put in place to keep the file around.
|
||
*/
|
||
static void
|
||
copy_to_local_namelist(struct remote_file *rfp)
|
||
{
|
||
char sendbuf[BUFSIZE];
|
||
char recvbuf[BUFSIZE];
|
||
char readbuf[READBUFSIZE];
|
||
int tty;
|
||
|
||
if (pc->flags & KERNEL_DEBUG_QUERY) {
|
||
/*
|
||
* Don't bother copying the kernel if the daemon can
|
||
* figure it out.
|
||
*/
|
||
BZERO(sendbuf, BUFSIZE);
|
||
BZERO(recvbuf, BUFSIZE);
|
||
sprintf(sendbuf, "DEBUGGING_SYMBOLS %s", rfp->filename);
|
||
remote_tcp_write_string(pc->sockfd, sendbuf);
|
||
remote_tcp_read_string(pc->sockfd, recvbuf, BUFSIZE-1, NIL_MODE());
|
||
if (strstr(recvbuf, "NO_DEBUG")) {
|
||
sprintf(readbuf, "%s@%s", rfp->filename, pc->server);
|
||
pc->namelist = readbuf;
|
||
no_debugging_data(FATAL);
|
||
}
|
||
}
|
||
|
||
pc->namelist = create_local_namelist(rfp);
|
||
|
||
if (pc->flags & NAMELIST_LOCAL)
|
||
return;
|
||
|
||
if ((pc->nfd = open(pc->namelist,
|
||
O_RDWR|O_CREAT|O_TRUNC, S_IRWXU)) < 0) {
|
||
pc->flags &= ~UNLINK_NAMELIST;
|
||
error(FATAL, "cannot create local copy of kernel (%s)\n",
|
||
pc->namelist);
|
||
}
|
||
|
||
tty = !(pc->flags & SILENT) && isatty(fileno(stdin));
|
||
|
||
if (!(pc->flags & NAMELIST_NO_GZIP)) {
|
||
copy_remote_gzip_file(rfp, pc->namelist, tty ?
|
||
"please wait... (copying remote kernel namelist: " : NULL);
|
||
if (tty)
|
||
fprintf(stderr,
|
||
"\r \r");
|
||
return;
|
||
}
|
||
|
||
if (copy_remote_file(rfp, pc->nfd, pc->namelist,
|
||
tty ? "please wait... (copying remote kernel namelist: " : NULL)) {
|
||
if (tty)
|
||
fprintf(stderr,
|
||
"\r \r");
|
||
} else
|
||
error(FATAL, "write to local copy of kernel namelist failed\n");
|
||
}
|
||
|
||
/*
|
||
* Try to create a file of the format: vmlinux@@hostname
|
||
* If it already exists, append "_0", "_1", etc. until one's not found.
|
||
*
|
||
* The file will be unlinked by display_sys_stats() the first time it's
|
||
* called.
|
||
*/
|
||
|
||
static char *
|
||
create_local_namelist(struct remote_file *rfp)
|
||
{
|
||
char buf[BUFSIZE];
|
||
char *p1;
|
||
int i, use_local_copy;
|
||
|
||
p1 = (char *)basename(rfp->filename);
|
||
|
||
sprintf(buf, "%s@%s", p1, pc->server);
|
||
for (i = 0, use_local_copy = FALSE; i >= 0; i++) {
|
||
if (file_exists(buf, NULL)) {
|
||
if (identical_namelist(buf, rfp)) {
|
||
use_local_copy = TRUE;
|
||
break;
|
||
}
|
||
sprintf(buf, "%s@%s_%d", p1,pc->server, i);
|
||
} else
|
||
break;
|
||
}
|
||
|
||
if ((p1 = (char *)malloc((size_t)(strlen(buf)+1))) == NULL)
|
||
error(FATAL, "cannot malloc temporary file name buffer\n");
|
||
|
||
strcpy(p1, buf);
|
||
|
||
if (use_local_copy)
|
||
pc->flags |= NAMELIST_LOCAL;
|
||
else
|
||
pc->flags |= UNLINK_NAMELIST;
|
||
|
||
return p1;
|
||
}
|
||
|
||
/*
|
||
* Before copying a kernel across, check whether a kernel of the same
|
||
* name is identical to the remote version.
|
||
*/
|
||
static int
|
||
identical_namelist(char *file, struct remote_file *rfp)
|
||
{
|
||
char *vers;
|
||
FILE *pipe;
|
||
struct stat sbuf;
|
||
long csum;
|
||
char sendbuf[BUFSIZE];
|
||
char recvbuf[BUFSIZE];
|
||
char readbuf[BUFSIZE*2];
|
||
|
||
if (stat(file, &sbuf) < 0)
|
||
return FALSE;
|
||
|
||
if (sbuf.st_size != rfp->size)
|
||
return FALSE;
|
||
|
||
if (remote_file_checksum(rfp) && file_checksum(file, &csum) &&
|
||
(csum == rfp->csum))
|
||
return TRUE;
|
||
|
||
BZERO(sendbuf, BUFSIZE);
|
||
BZERO(recvbuf, BUFSIZE);
|
||
BZERO(readbuf, BUFSIZE);
|
||
|
||
sprintf(sendbuf, "LINUX_VERSION %s", rfp->filename);
|
||
remote_tcp_write_string(pc->sockfd, sendbuf);
|
||
remote_tcp_read_string(pc->sockfd, recvbuf, BUFSIZE-1, NIL_MODE());
|
||
if (strstr(recvbuf, "<FAIL>"))
|
||
return FALSE;
|
||
|
||
vers = recvbuf;
|
||
|
||
sprintf(readbuf, "/usr/bin/strings %s | grep 'Linux version'",
|
||
file);
|
||
if ((pipe = popen(readbuf, "r"))) {
|
||
BZERO(readbuf, BUFSIZE);
|
||
if (fread(readbuf, sizeof(char), BUFSIZE-1, pipe) <= 0) {
|
||
pclose(pipe);
|
||
return FALSE;
|
||
}
|
||
pclose(pipe);
|
||
} else
|
||
return FALSE;
|
||
|
||
if (CRASHDEBUG(1)) {
|
||
fprintf(fp, "remote version: [%s]\n", vers);
|
||
fprintf(fp, "local version: [%s]\n", readbuf);
|
||
fprintf(fp, "%s vs. %s => %s\n",
|
||
file, rfp->filename,
|
||
STREQ(vers, readbuf) ? "IDENTICAL" : "DIFFERENT");
|
||
}
|
||
|
||
return (STREQ(vers, readbuf));
|
||
}
|
||
|
||
/*
|
||
* If a remote file exists, get its checksum and return TRUE.
|
||
*/
|
||
static int
|
||
remote_file_checksum(struct remote_file *rfp)
|
||
{
|
||
char sendbuf[BUFSIZE];
|
||
char recvbuf[BUFSIZE];
|
||
char *p1;
|
||
|
||
BZERO(sendbuf, BUFSIZE);
|
||
BZERO(recvbuf, BUFSIZE);
|
||
sprintf(sendbuf, "SUM %s", rfp->filename);
|
||
remote_tcp_write_string(pc->sockfd, sendbuf);
|
||
remote_tcp_read_string(pc->sockfd, recvbuf, BUFSIZE-1, NIL_MODE());
|
||
if (strstr(recvbuf, "<FAIL>")) {
|
||
error(INFO, "%s: does not exist on server %s\n",
|
||
rfp->filename, pc->server);
|
||
return FALSE;
|
||
}
|
||
strtok(recvbuf, " "); /* SUM */
|
||
p1 = strtok(NULL, " "); /* filename */
|
||
p1 = strtok(NULL, " "); /* checksum */
|
||
|
||
rfp->csum = htol(p1, FAULT_ON_ERROR, NULL);
|
||
return TRUE;
|
||
}
|
||
|
||
/*
|
||
* Copy a remote file locally, distinguishing it by appending an ampersand
|
||
* and the server name.
|
||
*
|
||
* If the kernel is requested, save the unlinked copy of the remote kernel
|
||
* in a local file, using the same name created by create_local_namelist().
|
||
*
|
||
* If a dumpfile, module, or any other file for that matter, append an
|
||
* ampersand plus the server name.
|
||
*
|
||
* Other files may have their local filename altered if a file of the
|
||
* same name exists with a different checksum.
|
||
*/
|
||
int
|
||
get_remote_file(struct remote_file *rfp)
|
||
{
|
||
int i;
|
||
char local[BUFSIZE];
|
||
char readbuf[READBUFSIZE];
|
||
char *p1;
|
||
struct load_module *lm;
|
||
int cnt, sfd, err, retval;
|
||
long csum;
|
||
|
||
if (!REMOTE()) {
|
||
error(INFO, "no remote files in use\n");
|
||
return FALSE;
|
||
}
|
||
|
||
if (rfp->local)
|
||
goto generic_file_save;
|
||
|
||
sprintf(readbuf, "%s@%s", pc->server_memsrc, pc->server);
|
||
if (STREQ(rfp->filename, "dumpfile") ||
|
||
STREQ(rfp->filename, pc->server_memsrc) ||
|
||
STREQ(rfp->filename, basename(pc->server_memsrc)) ||
|
||
STREQ(rfp->filename, readbuf))
|
||
goto dumpfile_save;
|
||
|
||
sprintf(readbuf, "%s", pc->namelist);
|
||
if ((p1 = strstr(readbuf, "@")))
|
||
*p1 = NULLCHAR;
|
||
if (STREQ(rfp->filename, "kernel") ||
|
||
STREQ(rfp->filename, pc->namelist) ||
|
||
STREQ(rfp->filename, pc->server_namelist) ||
|
||
STREQ(rfp->filename, readbuf))
|
||
goto kernel_save;
|
||
|
||
|
||
if (STREQ(rfp->filename, "modules")) {
|
||
for (i = 0; i < kt->mods_installed; i++) {
|
||
lm = &st->load_modules[i];
|
||
if (lm->mod_flags & MOD_REMOTE) {
|
||
fprintf(fp, "%s module saved as: %s\n",
|
||
lm->mod_name, lm->mod_namelist);
|
||
lm->mod_flags &= ~MOD_REMOTE;
|
||
}
|
||
}
|
||
return TRUE;
|
||
}
|
||
|
||
if (is_module_name(rfp->filename, NULL, &lm)) {
|
||
if (lm->mod_flags & MOD_REMOTE) {
|
||
fprintf(fp, "%s module saved as: %s\n",
|
||
lm->mod_name, lm->mod_namelist);
|
||
lm->mod_flags &= ~MOD_REMOTE;
|
||
}
|
||
return TRUE;
|
||
}
|
||
|
||
strcpy(local, rfp->filename);
|
||
if ((p1 = strstr(local, ".o"))) {
|
||
*p1 = NULLCHAR;
|
||
if (is_module_name(basename(local), NULL, &lm)) {
|
||
if (lm->mod_flags & MOD_REMOTE) {
|
||
fprintf(fp, "%s module saved as: %s\n",
|
||
lm->mod_name, lm->mod_namelist);
|
||
lm->mod_flags &= ~MOD_REMOTE;
|
||
return TRUE;
|
||
}
|
||
}
|
||
}
|
||
|
||
generic_file_save:
|
||
|
||
cnt = 0;
|
||
sprintf(local, "%s@%s", basename(rfp->filename), pc->server);
|
||
while (file_exists(local, NULL)) {
|
||
if (CRASHDEBUG(1))
|
||
fprintf(fp, "%s already exists in this directory\n",
|
||
local);
|
||
if (file_checksum(local, &csum) && (csum == rfp->csum)) {
|
||
if (CRASHDEBUG(1))
|
||
error(NOTE,
|
||
"local %s checksum matches -- using it\n",
|
||
local);
|
||
strcpy(rfp->local, local);
|
||
return TRUE;
|
||
}
|
||
sprintf(local, "%s@%s_%d",
|
||
basename(rfp->filename), pc->server, ++cnt);
|
||
}
|
||
|
||
if (!remote_file_open(rfp)) {
|
||
error(INFO, "daemon cannot open: %s\n", rfp->filename);
|
||
return FALSE;
|
||
}
|
||
|
||
if ((sfd = open(local, O_RDWR|O_CREAT|O_TRUNC, S_IRWXU)) < 0) {
|
||
error(INFO, "open: %s: %s\n", local, strerror(errno));
|
||
remote_file_close(rfp);
|
||
return FALSE;
|
||
}
|
||
|
||
if (copy_remote_file(rfp, sfd, local, rfp->flags & REMOTE_VERBOSE ?
|
||
"please wait... (copying remote file: " : NULL)) {
|
||
if (rfp->flags & REMOTE_VERBOSE)
|
||
fprintf(stderr,
|
||
"\rremote file saved as: \"%s\" \n",
|
||
local);
|
||
retval = TRUE;
|
||
rfp->flags |= REMOTE_COPY_DONE;
|
||
} else {
|
||
fprintf(stderr,
|
||
"\r%s NOT saved \n",
|
||
rfp->filename);
|
||
retval = FALSE;
|
||
}
|
||
|
||
close(sfd);
|
||
remote_file_close(rfp);
|
||
|
||
if (cnt)
|
||
strcpy(rfp->local, local);
|
||
|
||
return retval;
|
||
|
||
kernel_save:
|
||
|
||
if (pc->flags & NAMELIST_SAVED) {
|
||
error(INFO, "\"%s\" is already saved\n", pc->namelist);
|
||
return FALSE;
|
||
}
|
||
|
||
if (pc->flags & NAMELIST_LOCAL) {
|
||
error(INFO, "\"%s\" is a local file\n", pc->namelist);
|
||
return FALSE;
|
||
}
|
||
|
||
if ((sfd = open(pc->namelist, O_RDWR|O_CREAT|O_TRUNC, S_IRWXU)) < 0) {
|
||
error(INFO, "open: %s: %s\n", pc->namelist, strerror(errno));
|
||
return FALSE;
|
||
}
|
||
|
||
err = 0;
|
||
lseek(sfd, 0, SEEK_SET);
|
||
lseek(pc->nfd, 0, SEEK_SET);
|
||
|
||
while ((cnt = read(pc->nfd, readbuf, READBUFSIZE)) > 0) {
|
||
if (write(sfd, readbuf, cnt) != cnt) {
|
||
error(INFO, "write:%s: %s\n",
|
||
pc->namelist, strerror(errno));
|
||
err++;
|
||
break;
|
||
}
|
||
}
|
||
|
||
close(sfd);
|
||
|
||
if (err) {
|
||
fprintf(fp, "%s NOT saved\n", pc->namelist);
|
||
unlink(pc->namelist);
|
||
retval = FALSE;
|
||
} else {
|
||
fprintf(fp, "kernel saved as: \"%s\"\n", pc->namelist);
|
||
close(pc->nfd);
|
||
pc->nfd = -1;
|
||
pc->flags |= NAMELIST_SAVED;
|
||
retval = TRUE;
|
||
}
|
||
|
||
return (retval);
|
||
|
||
dumpfile_save:
|
||
|
||
if (pc->flags & DUMPFILE_SAVED) {
|
||
error(INFO, "\"%s@%s\" is already saved\n",
|
||
basename(pc->server_memsrc), pc->server);
|
||
return FALSE;
|
||
}
|
||
|
||
if (pc->flags2 & MEMSRC_LOCAL) {
|
||
error(INFO, "%s is a local file\n", pc->dumpfile);
|
||
return FALSE;
|
||
}
|
||
|
||
if (!(REMOTE_DUMPFILE())) {
|
||
error(INFO, "%s is not a dumpfile\n", pc->server_memsrc);
|
||
return FALSE;
|
||
}
|
||
|
||
sprintf(local, "%s@%s", basename(pc->server_memsrc), pc->server);
|
||
|
||
if (file_exists(local, NULL)) {
|
||
error(INFO, "%s already exists in this directory\n",
|
||
local);
|
||
return FALSE;
|
||
}
|
||
|
||
rfp->filename = pc->server_memsrc;
|
||
if (!remote_file_open(rfp)) {
|
||
error(INFO, "daemon cannot open: %s\n", pc->server_memsrc);
|
||
return FALSE;
|
||
}
|
||
|
||
if ((sfd = open(local, O_RDWR|O_CREAT|O_TRUNC, S_IRWXU)) < 0) {
|
||
error(INFO, "open: %s: %s\n", local, strerror(errno));
|
||
remote_file_close(rfp);
|
||
return FALSE;
|
||
}
|
||
|
||
if (copy_remote_file(rfp, sfd, local,
|
||
"please wait... (copying remote dumpfile: ")) {
|
||
fprintf(stderr,
|
||
"\rdumpfile saved as: \"%s\" \n",
|
||
local);
|
||
pc->flags |= DUMPFILE_SAVED;
|
||
retval = TRUE;
|
||
} else {
|
||
fprintf(stderr,
|
||
"\r%s NOT saved \n",
|
||
pc->server_memsrc);
|
||
retval = FALSE;
|
||
}
|
||
|
||
close(sfd);
|
||
remote_file_close(rfp);
|
||
|
||
return (retval);
|
||
|
||
}
|
||
|
||
/*
|
||
* Query the remote daemon for the kernel name that is running.
|
||
*/
|
||
static int
|
||
remote_find_booted_kernel(struct remote_file *rfp)
|
||
{
|
||
char sendbuf[BUFSIZE];
|
||
char recvbuf[BUFSIZE];
|
||
char *p1;
|
||
|
||
BZERO(sendbuf, BUFSIZE);
|
||
BZERO(recvbuf, BUFSIZE);
|
||
sprintf(sendbuf, "FIND_BOOTED_KERNEL");
|
||
remote_tcp_write_string(pc->sockfd, sendbuf);
|
||
remote_tcp_read_string(pc->sockfd, recvbuf, BUFSIZE-1, NIL_MODE());
|
||
strtok(recvbuf, " "); /* FIND_BOOTED_KERNEL */
|
||
p1 = strtok(NULL, " "); /* filename */
|
||
if (STREQ(p1, "<FAIL>"))
|
||
return FALSE;
|
||
strcpy(rfp->filename, p1);
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
static int
|
||
remote_lkcd_dump_init(void)
|
||
{
|
||
char sendbuf[BUFSIZE];
|
||
char recvbuf[BUFSIZE];
|
||
char *p1, *p2, *p3;
|
||
|
||
BZERO(sendbuf, BUFSIZE);
|
||
BZERO(recvbuf, BUFSIZE);
|
||
sprintf(sendbuf, "LKCD_DUMP_INIT %d %s", pc->rmfd, pc->server_memsrc);
|
||
remote_tcp_write_string(pc->sockfd, sendbuf);
|
||
remote_tcp_read_string(pc->sockfd, recvbuf, BUFSIZE-1, NIL_MODE());
|
||
if (strstr(recvbuf, "<FAIL>"))
|
||
return FALSE;
|
||
|
||
p1 = strstr(recvbuf, "panic_task: ");
|
||
p2 = strstr(recvbuf, "panicmsg: ");
|
||
|
||
if (p1) {
|
||
p1 += strlen("panic_task: ");
|
||
p3 = strstr(p1, "\n");
|
||
*p3 = NULLCHAR;
|
||
tt->panic_task = htol(p1, FAULT_ON_ERROR, NULL);
|
||
if (CRASHDEBUG(1))
|
||
fprintf(fp, "panic_task: %lx\n", tt->panic_task);
|
||
}
|
||
if (p2) {
|
||
p2 += strlen("panicmsg: ");
|
||
if (CRASHDEBUG(1))
|
||
fprintf(fp, "panicmsg: %s", p2);
|
||
}
|
||
|
||
set_remote_lkcd_panic_data(tt->panic_task, p2);
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
static int
|
||
remote_s390_dump_init(void)
|
||
{
|
||
char sendbuf[BUFSIZE];
|
||
char recvbuf[BUFSIZE];
|
||
char *p1, *p2, *p3;
|
||
|
||
BZERO(sendbuf, BUFSIZE);
|
||
BZERO(recvbuf, BUFSIZE);
|
||
sprintf(sendbuf, "S390_DUMP_INIT %d %s", pc->rmfd, pc->server_memsrc);
|
||
remote_tcp_write_string(pc->sockfd, sendbuf);
|
||
remote_tcp_read_string(pc->sockfd, recvbuf, BUFSIZE-1, NIL_MODE());
|
||
if (strstr(recvbuf, "<FAIL>"))
|
||
return FALSE;
|
||
|
||
p1 = strstr(recvbuf, "panic_task: ");
|
||
p2 = strstr(recvbuf, "panicmsg: ");
|
||
|
||
if (p1) {
|
||
p1 += strlen("panic_task: ");
|
||
p3 = strstr(p1, "\n");
|
||
*p3 = NULLCHAR;
|
||
tt->panic_task = htol(p1, FAULT_ON_ERROR, NULL);
|
||
if (CRASHDEBUG(1))
|
||
fprintf(fp, "panic_task: %lx\n", tt->panic_task);
|
||
}
|
||
if (p2) {
|
||
p2 += strlen("panicmsg: ");
|
||
if (CRASHDEBUG(1))
|
||
fprintf(fp, "panicmsg: %s", p2);
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
static int
|
||
remote_netdump_init(void)
|
||
{
|
||
char sendbuf[BUFSIZE];
|
||
char recvbuf[BUFSIZE];
|
||
char *p1, *p2;
|
||
ulong panic_task;
|
||
|
||
BZERO(sendbuf, BUFSIZE);
|
||
BZERO(recvbuf, BUFSIZE);
|
||
sprintf(sendbuf, "NETDUMP_INIT %d %s", pc->rmfd, pc->server_memsrc);
|
||
remote_tcp_write_string(pc->sockfd, sendbuf);
|
||
remote_tcp_read_string(pc->sockfd, recvbuf, BUFSIZE-1, NIL_MODE());
|
||
if (strstr(recvbuf, "<FAIL>"))
|
||
return FALSE;
|
||
|
||
p1 = strstr(recvbuf, "panic_task: ");
|
||
|
||
if (p1) {
|
||
p1 += strlen("panic_task: ");
|
||
p2 = strstr(p1, "\n");
|
||
*p2 = NULLCHAR;
|
||
panic_task = htol(p1, FAULT_ON_ERROR, NULL);
|
||
tt->panic_task = panic_task; /* kludge */
|
||
if (CRASHDEBUG(1))
|
||
fprintf(fp, "panic_task: %lx\n", tt->panic_task);
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
uint
|
||
remote_page_size(void)
|
||
{
|
||
char sendbuf[BUFSIZE];
|
||
char recvbuf[BUFSIZE];
|
||
char *p1, *p2, *p3;
|
||
uint psz;
|
||
|
||
BZERO(sendbuf, BUFSIZE);
|
||
BZERO(recvbuf, BUFSIZE);
|
||
|
||
if (REMOTE_ACTIVE())
|
||
sprintf(sendbuf, "PAGESIZE LIVE");
|
||
else if (REMOTE_PAUSED())
|
||
sprintf(sendbuf, "PAGESIZE NIL");
|
||
else if (pc->flags & REM_NETDUMP)
|
||
sprintf(sendbuf, "PAGESIZE NETDUMP");
|
||
else if (pc->flags & REM_MCLXCD)
|
||
sprintf(sendbuf, "PAGESIZE MCLXCD");
|
||
else if (pc->flags & REM_LKCD)
|
||
sprintf(sendbuf, "PAGESIZE LKCD");
|
||
else if (pc->flags & REM_S390D)
|
||
sprintf(sendbuf, "PAGESIZE S390D");
|
||
else
|
||
error(FATAL,
|
||
"cannot determine remote page size (unknown memory source)\n");
|
||
|
||
remote_tcp_write_string(pc->sockfd, sendbuf);
|
||
remote_tcp_read_string(pc->sockfd, recvbuf, BUFSIZE-1, NIL_MODE());
|
||
if (strstr(recvbuf, "FAIL"))
|
||
error(FATAL, "cannot determine remote page size\n");
|
||
strtok(recvbuf, " "); /* PAGESIZE */
|
||
p1 = strtok(NULL, " "); /* LIVE, MCLXCD or LKCD */
|
||
p1 = strtok(NULL, " "); /* page size */
|
||
p2 = strtok(NULL, " "); /* remote type */
|
||
p3 = strtok(NULL, " "); /* number of Cpus */
|
||
psz = atoi(p1);
|
||
|
||
if (psz > MAXRECVBUFSIZE)
|
||
error(FATAL,
|
||
"remote page size %d is larger than MAXRECVBUFSIZE!\n", psz);
|
||
|
||
if (p2) {
|
||
strncpy(rc->remote_type, p2, sizeof(rc->remote_type) - 1);
|
||
rc->remote_type[sizeof(rc->remote_type) - 1] = 0;
|
||
}
|
||
if (p3)
|
||
rc->n_cpus = atoi(p3);
|
||
|
||
return psz;
|
||
}
|
||
|
||
/*
|
||
* Copy a remote file to a local file, closing the passed-in fd when done.
|
||
* A running tally of percentage-done numbers can optionally be displayed.
|
||
*/
|
||
static int
|
||
copy_remote_file(struct remote_file *rfp, int fd, char *file, char *ttystr)
|
||
{
|
||
char sendbuf[BUFSIZE];
|
||
char recvbuf[BUFSIZE*2];
|
||
char readbuf[READBUFSIZE];
|
||
char *bufptr;
|
||
long pct, last;
|
||
ulong size, offset, filesize;
|
||
ulong ret, req, tot;
|
||
int sysret ATTRIBUTE_UNUSED;
|
||
ssize_t bytes ATTRIBUTE_UNUSED;
|
||
|
||
last = -1;
|
||
lseek(fd, 0, SEEK_SET);
|
||
filesize = rfp->size;
|
||
|
||
for (offset = 0; offset < filesize; ) {
|
||
|
||
size = MIN(filesize-offset, pc->rcvbufsize);
|
||
|
||
BZERO(sendbuf, BUFSIZE);
|
||
sprintf(sendbuf, "READ %d %lx %ld", rfp->fd, offset, size);
|
||
bytes = write(pc->sockfd, sendbuf, strlen(sendbuf) + 1);
|
||
|
||
bzero(readbuf, READBUFSIZE);
|
||
|
||
req = size;
|
||
tot = 0;
|
||
|
||
sprintf(recvbuf, "%s:FAIL", sendbuf);
|
||
bufptr = readbuf;
|
||
while (req) {
|
||
ret = recv(pc->sockfd, bufptr, req, 0);
|
||
if (!tot && STRNEQ(bufptr, recvbuf)) {
|
||
tot = -1;
|
||
break;
|
||
}
|
||
req -= ret;
|
||
tot += ret;
|
||
bufptr += ret;
|
||
}
|
||
|
||
if (tot == -1)
|
||
break;
|
||
|
||
if (write(fd, readbuf, size) != size) {
|
||
error(INFO,
|
||
"%swrite to local file \"%s\" failed",
|
||
ttystr ? "\n" : "", file);
|
||
close(fd);
|
||
return FALSE;
|
||
}
|
||
|
||
offset += tot;
|
||
|
||
if (ttystr) {
|
||
pct = (offset*100)/filesize;
|
||
|
||
if (pct > last) { /* readline work-around... */
|
||
if (last < 0)
|
||
sprintf(readbuf, "echo -n \'%s0%%)\'",
|
||
ttystr);
|
||
else if (last >= 0 && last < 10)
|
||
sprintf(readbuf,
|
||
"echo -e -n \"\\b\\b\\b%ld%%)\"",
|
||
pct);
|
||
else if (last < 100)
|
||
sprintf(readbuf,
|
||
"echo -e -n \"\\b\\b\\b\\b%ld%%)\"",
|
||
pct);
|
||
sysret = system(readbuf);
|
||
last = pct;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (offset != filesize) {
|
||
error(INFO, "%swrite to local file \"%s\" failed",
|
||
ttystr ? "\n" : "", file);
|
||
close(fd);
|
||
return FALSE;
|
||
}
|
||
|
||
fsync(fd);
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
/*
|
||
* Copy a remote file to a local file, closing the passed-in fd when done.
|
||
* A running tally of percentage-done numbers can optionally be displayed.
|
||
*/
|
||
|
||
static void
|
||
copy_remote_gzip_file(struct remote_file *rfp, char *file, char *ttystr)
|
||
{
|
||
int done;
|
||
char sendbuf[BUFSIZE];
|
||
char readbuf[READBUFSIZE];
|
||
char gziphdr[DATA_HDRSIZE];
|
||
char *bufptr, *p1;
|
||
FILE *pipe;
|
||
size_t gtot;
|
||
struct stat sbuf;
|
||
ulong pct, ret, req, tot, total;
|
||
|
||
sprintf(readbuf, "/usr/bin/gunzip > %s", pc->namelist);
|
||
if ((pipe = popen(readbuf, "w")) == NULL)
|
||
error(FATAL, "cannot open pipe to create %s\n", pc->namelist);
|
||
|
||
BZERO(sendbuf, BUFSIZE);
|
||
sprintf(sendbuf, "READ_GZIP %ld %s", pc->rcvbufsize, rfp->filename);
|
||
remote_tcp_write_string(pc->sockfd, sendbuf);
|
||
|
||
bzero(readbuf, READBUFSIZE);
|
||
|
||
done = total = 0;
|
||
gtot = 0;
|
||
|
||
while (!done) {
|
||
|
||
req = pc->rcvbufsize;
|
||
bufptr = readbuf;
|
||
tot = 0;
|
||
|
||
while (req) {
|
||
ret = (ulong)recv(pc->sockfd, bufptr, req, 0);
|
||
if (!tot) {
|
||
if (STRNEQ(bufptr, FAILMSG)) {
|
||
fprintf(fp,
|
||
"copy_remote_gzip_file: %s\n",
|
||
bufptr);
|
||
tot = -1;
|
||
break;
|
||
}
|
||
if (STRNEQ(bufptr, DONEMSG) ||
|
||
STRNEQ(bufptr, DATAMSG)) {
|
||
BCOPY(bufptr, gziphdr, DATA_HDRSIZE);
|
||
if (CRASHDEBUG(1))
|
||
fprintf(fp,
|
||
"copy_remote_gzip_file: [%s]\n",
|
||
gziphdr);
|
||
p1 = strtok(gziphdr, " "); /* DONE */
|
||
if (STREQ(p1, "DONE"))
|
||
done = TRUE;
|
||
p1 = strtok(NULL, " "); /* count */
|
||
gtot = atol(p1);
|
||
total += gtot;
|
||
}
|
||
}
|
||
req -= ret;
|
||
tot += ret;
|
||
bufptr += ret;
|
||
}
|
||
|
||
if (tot == -1)
|
||
break;
|
||
|
||
if (fwrite(&readbuf[DATA_HDRSIZE], sizeof(char), gtot, pipe)
|
||
!= gtot)
|
||
error(FATAL, "fwrite to %s failed\n", pc->namelist);
|
||
|
||
if (ttystr && (stat(pc->namelist, &sbuf) == 0)) {
|
||
pct = (sbuf.st_size * 100)/rfp->size;
|
||
fprintf(stderr, "\r%s%ld%%)%s",
|
||
ttystr, pct, CRASHDEBUG(1) ? "\n" : "");
|
||
}
|
||
}
|
||
|
||
if (CRASHDEBUG(1))
|
||
fprintf(fp, "copy_remote_gzip_file: GZIP total: %ld\n", total);
|
||
|
||
pclose(pipe);
|
||
}
|
||
|
||
/*
|
||
* Set up to have get_remote_file() copy the remote module locally.
|
||
* If it's already here, no copy is done.
|
||
*/
|
||
int
|
||
find_remote_module_objfile(struct load_module *lm, char *module, char *retbuf)
|
||
{
|
||
int absolute;
|
||
char sendbuf[BUFSIZE];
|
||
char recvbuf[BUFSIZE];
|
||
char local[BUFSIZE];
|
||
char found[BUFSIZE];
|
||
char *p1;
|
||
long csum;
|
||
struct remote_file remote_file, *rfp;
|
||
|
||
rfp = &remote_file;
|
||
BZERO(rfp, sizeof(struct remote_file));
|
||
|
||
absolute = (*module == '/');
|
||
|
||
if (absolute) {
|
||
if ((p1 = strstr(module, "@"))) {
|
||
*p1 = NULLCHAR;
|
||
} else {
|
||
error(FATAL,
|
||
"module file name must have \"@server-name\" attached\n");
|
||
}
|
||
|
||
sprintf(local, "%s@%s", basename(module), pc->server);
|
||
rfp->filename = module;
|
||
rfp->local = local;
|
||
|
||
if (!remote_file_checksum(rfp)) {
|
||
error(INFO, "%s: does not exist on server %s\n",
|
||
module, pc->server);
|
||
return FALSE;
|
||
}
|
||
} else {
|
||
if ((p1 = strstr(module, "@")))
|
||
*p1 = NULLCHAR;
|
||
|
||
sprintf(local, "%s@%s", module, pc->server);
|
||
|
||
BZERO(sendbuf, BUFSIZE);
|
||
BZERO(recvbuf, BUFSIZE);
|
||
sprintf(sendbuf, "FIND_MODULE %s %s",
|
||
kt->utsname.release, module);
|
||
remote_tcp_write_string(pc->sockfd, sendbuf);
|
||
remote_tcp_read_string(pc->sockfd, recvbuf, BUFSIZE-1, NIL_MODE());
|
||
if (strstr(recvbuf, "<FAIL>")) {
|
||
fprintf(fp, "find_remote_module_objfile: [%s]\n",
|
||
recvbuf);
|
||
return FALSE;
|
||
}
|
||
strtok(recvbuf, " "); /* FIND_MODULE */
|
||
p1 = strtok(NULL, " "); /* release */
|
||
p1 = strtok(NULL, " "); /* module */
|
||
strcpy(found, strtok(NULL, " ")); /* resultant path */
|
||
p1 = strtok(NULL, " "); /* checksum */
|
||
csum = htol(p1, FAULT_ON_ERROR, NULL);
|
||
|
||
rfp->filename = found;
|
||
rfp->local = local;
|
||
rfp->csum = csum;
|
||
}
|
||
|
||
if (get_remote_file(rfp)) {
|
||
if (!is_elf_file(rfp->local)) {
|
||
error(INFO,
|
||
"%s@%s: not an ELF format object file\n",
|
||
rfp->filename, pc->server);
|
||
return FALSE;
|
||
}
|
||
strcpy(retbuf, rfp->local);
|
||
if (rfp->flags & REMOTE_COPY_DONE) {
|
||
lm->mod_flags |= MOD_REMOTE;
|
||
pc->flags |= UNLINK_MODULES;
|
||
}
|
||
return TRUE;
|
||
}
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
/*
|
||
* Tell the daemon to free the current dumpfile memory.
|
||
*/
|
||
int
|
||
remote_free_memory(void)
|
||
{
|
||
char sendbuf[BUFSIZE];
|
||
char recvbuf[BUFSIZE];
|
||
char *type, *p1;
|
||
|
||
if (pc->flags & REM_NETDUMP)
|
||
type = "NETDUMP";
|
||
else if (pc->flags & REM_MCLXCD)
|
||
type = "MCLXCD";
|
||
else if (pc->flags & REM_LKCD)
|
||
type = "LKCD";
|
||
else if (pc->flags & REM_S390D)
|
||
type = "S390D";
|
||
else
|
||
return 0;
|
||
|
||
BZERO(sendbuf, BUFSIZE);
|
||
BZERO(recvbuf, BUFSIZE);
|
||
sprintf(sendbuf, "MEMORY FREE %s", type);
|
||
remote_tcp_write_string(pc->sockfd, sendbuf);
|
||
remote_tcp_read_string(pc->sockfd, recvbuf, BUFSIZE-1, NIL_MODE());
|
||
p1 = strtok(recvbuf, " "); /* MEMORY */
|
||
p1 = strtok(NULL, " "); /* FREE */
|
||
p1 = strtok(NULL, " "); /* MCLXCD, LKCD etc. */
|
||
p1 = strtok(NULL, " "); /* pages */
|
||
if (STREQ(p1, "<FAIL>"))
|
||
return 0;
|
||
|
||
return(atol(p1));
|
||
}
|
||
|
||
/*
|
||
* Return the number of dumpfile pages used by the daemon.
|
||
*/
|
||
int
|
||
remote_memory_used(void)
|
||
{
|
||
char sendbuf[BUFSIZE];
|
||
char recvbuf[BUFSIZE];
|
||
char *type, *p1;
|
||
|
||
if (pc->flags & REM_NETDUMP)
|
||
type = "NETDUMP";
|
||
else if (pc->flags & REM_MCLXCD)
|
||
type = "MCLXCD";
|
||
else if (pc->flags & REM_LKCD)
|
||
type = "LKCD";
|
||
else if (pc->flags & REM_S390D)
|
||
type = "S390D";
|
||
else
|
||
return 0;
|
||
|
||
BZERO(sendbuf, BUFSIZE);
|
||
BZERO(recvbuf, BUFSIZE);
|
||
sprintf(sendbuf, "MEMORY USED %s", type);
|
||
remote_tcp_write_string(pc->sockfd, sendbuf);
|
||
remote_tcp_read_string(pc->sockfd, recvbuf, BUFSIZE-1, NIL_MODE());
|
||
p1 = strtok(recvbuf, " "); /* MEMORY */
|
||
p1 = strtok(NULL, " "); /* FREE */
|
||
p1 = strtok(NULL, " "); /* MCLXCD, LKCD, etc. */
|
||
p1 = strtok(NULL, " "); /* pages */
|
||
if (STREQ(p1, "<FAIL>"))
|
||
return 0;
|
||
|
||
return(atol(p1));
|
||
}
|
||
|
||
/*
|
||
* Have the daemon return the output of vas_memory_dump(), lkcd_memory_dump().
|
||
* or dump_lkcd_environment()
|
||
*/
|
||
int
|
||
remote_memory_dump(int verbose)
|
||
{
|
||
char sendbuf[BUFSIZE];
|
||
char readbuf[READBUFSIZE];
|
||
char datahdr[DATA_HDRSIZE];
|
||
char *type, *bufptr, *p1;
|
||
ulong done, total;
|
||
ulong ret, req, tot;
|
||
size_t dtot;
|
||
|
||
if (pc->flags & REM_NETDUMP)
|
||
type = "NETDUMP";
|
||
else if (pc->flags & REM_MCLXCD)
|
||
type = "MCLXCD";
|
||
else if (pc->flags & REM_LKCD)
|
||
type = "LKCD";
|
||
else if (pc->flags & REM_S390D)
|
||
type = "S390D";
|
||
else
|
||
return 0;
|
||
|
||
BZERO(sendbuf, BUFSIZE);
|
||
sprintf(sendbuf, "MEMORY_DUMP %ld %s%s", pc->rcvbufsize, type,
|
||
verbose ? "_VERBOSE" : "");
|
||
remote_tcp_write_string(pc->sockfd, sendbuf);
|
||
|
||
bzero(readbuf, READBUFSIZE);
|
||
done = total = 0;
|
||
dtot = 0;
|
||
|
||
while (!done) {
|
||
|
||
req = pc->rcvbufsize;
|
||
bufptr = readbuf;
|
||
tot = 0;
|
||
|
||
while (req) {
|
||
ret = recv(pc->sockfd, bufptr, req, 0);
|
||
if (!tot) {
|
||
if (STRNEQ(bufptr, FAILMSG)) {
|
||
fprintf(fp,
|
||
"remote_memory_dump: %s\n",
|
||
bufptr);
|
||
tot = -1;
|
||
break;
|
||
}
|
||
if (STRNEQ(bufptr, DONEMSG) ||
|
||
STRNEQ(bufptr, DATAMSG)) {
|
||
BCOPY(bufptr, datahdr, DATA_HDRSIZE);
|
||
if (CRASHDEBUG(1))
|
||
fprintf(fp,
|
||
"remote_memory_dump: [%s]\n",
|
||
datahdr);
|
||
p1 = strtok(datahdr, " "); /* DONE */
|
||
if (STREQ(p1, "DONE"))
|
||
done = TRUE;
|
||
p1 = strtok(NULL, " "); /* count */
|
||
dtot = atol(p1);
|
||
total += dtot;
|
||
}
|
||
}
|
||
req -= ret;
|
||
tot += ret;
|
||
bufptr += ret;
|
||
}
|
||
|
||
if (tot == -1)
|
||
break;
|
||
|
||
if (fwrite(&readbuf[DATA_HDRSIZE], sizeof(char), dtot, fp)
|
||
!= dtot)
|
||
error(FATAL, "fwrite to %s failed\n", pc->namelist);
|
||
}
|
||
|
||
return 1;
|
||
}
|
||
|
||
/*
|
||
* Read memory from the remote memory source. The remote file descriptor
|
||
* is abstracted to allow for a common /dev/mem-/dev/kmem call. Since
|
||
* this is only called from read_daemon(), the request can never exceed
|
||
* a page in length.
|
||
*/
|
||
int
|
||
remote_memory_read(int rfd, char *buffer, int cnt, physaddr_t address, int vcpu)
|
||
{
|
||
char sendbuf[BUFSIZE];
|
||
char datahdr[DATA_HDRSIZE];
|
||
char *p1;
|
||
int ret, tot;
|
||
ulong addr;
|
||
|
||
addr = (ulong)address; /* may be virtual */
|
||
|
||
BZERO(sendbuf, BUFSIZE);
|
||
if (pc->flags & REM_NETDUMP) {
|
||
sprintf(sendbuf, "READ_NETDUMP %lx %d", addr, cnt);
|
||
} else if (pc->flags & REM_MCLXCD)
|
||
sprintf(sendbuf, "READ_MCLXCD %lx %d", addr, cnt);
|
||
else if (pc->flags & REM_LKCD)
|
||
sprintf(sendbuf, "READ_LKCD %d %lx %d", rfd, addr, cnt);
|
||
else if (pc->flags & REM_S390D)
|
||
sprintf(sendbuf, "READ_S390D %d %lx %d", rfd, addr, cnt);
|
||
else if (vcpu >= 0)
|
||
sprintf(sendbuf, "READ_LIVE %d %lx %d %d", rfd, addr, cnt, vcpu);
|
||
else
|
||
sprintf(sendbuf, "READ_LIVE %d %lx %d", rfd, addr, cnt);
|
||
|
||
if (remote_tcp_write_string(pc->sockfd, sendbuf))
|
||
return -1;
|
||
|
||
/*
|
||
* Read request will come back with a singular header
|
||
* followed by the data.
|
||
*/
|
||
BZERO(datahdr, DATA_HDRSIZE);
|
||
ret = remote_tcp_read_string(pc->sockfd, datahdr, DATA_HDRSIZE, 1);
|
||
if (ret <= 0)
|
||
return -1;
|
||
if (CRASHDEBUG(3))
|
||
fprintf(fp, "remote_memory_read: [%s]\n", datahdr);
|
||
if (STRNEQ(datahdr, FAILMSG)) {
|
||
p1 = strtok(datahdr, " "); /* FAIL */
|
||
p1 = strtok(NULL, " "); /* errno */
|
||
errno = atoi(p1);
|
||
return -1;
|
||
}
|
||
|
||
if (!STRNEQ(datahdr, DONEMSG) && !STRNEQ(datahdr, DATAMSG)) {
|
||
error(INFO, "out of sync with remote memory source\n");
|
||
return -1;
|
||
}
|
||
|
||
p1 = strtok(datahdr, " "); /* DONE */
|
||
p1 = strtok(NULL, " "); /* count */
|
||
tot = atol(p1);
|
||
|
||
if (cnt != tot) {
|
||
error(FATAL,
|
||
"requested %d bytes remote memory return %d bytes\n",
|
||
cnt, tot);
|
||
return -1;
|
||
}
|
||
|
||
ret = remote_tcp_read(pc->sockfd, buffer, tot);
|
||
if (ret != tot) {
|
||
error(FATAL,
|
||
"requested %d bytes remote memory return %d bytes\n",
|
||
ret, tot);
|
||
return -1;
|
||
}
|
||
return tot;
|
||
}
|
||
|
||
/*
|
||
* If a command was interrupted locally, there may be leftover data waiting
|
||
* to be read.
|
||
*/
|
||
void
|
||
remote_clear_pipeline(void)
|
||
{
|
||
int ret;
|
||
fd_set rfds;
|
||
char recvbuf[READBUFSIZE];
|
||
struct timeval tv;
|
||
|
||
tv.tv_sec = 0;
|
||
tv.tv_usec = 0;
|
||
|
||
FD_ZERO(&rfds);
|
||
FD_SET(pc->sockfd, &rfds);
|
||
ret = select(pc->sockfd+1, &rfds, NULL, NULL, &tv);
|
||
|
||
if (FD_ISSET(pc->sockfd, &rfds)) {
|
||
ret = recv(pc->sockfd, recvbuf, pc->rcvbufsize, 0);
|
||
if (CRASHDEBUG(1))
|
||
error(INFO,
|
||
"remote_clear_pipeline(%d): %d bytes discarded\n",
|
||
pc->sockfd, ret);
|
||
}
|
||
}
|
||
|
||
/*
|
||
* Attempt to run the user-entered command on the remote system.
|
||
*/
|
||
int
|
||
remote_execute(void)
|
||
{
|
||
char command[BUFSIZE];
|
||
char sendbuf[BUFSIZE*2];
|
||
char readbuf[READBUFSIZE];
|
||
char datahdr[DATA_HDRSIZE];
|
||
char *bufptr, *p1;
|
||
ulong done, total;
|
||
ulong ret, req, tot;
|
||
size_t dtot;
|
||
|
||
if (!STRNEQ(args[0], "@") || strlen(args[0]) == 1)
|
||
return FALSE;
|
||
|
||
shift_string_left(concat_args(command, 0, FALSE), 1);
|
||
|
||
if (QUOTED_STRING(command))
|
||
strip_ending_char(strip_beginning_char(command, '"'), '"');
|
||
|
||
if (CRASHDEBUG(1))
|
||
error(INFO, "remote command: %s\n", command);
|
||
|
||
BZERO(sendbuf, BUFSIZE);
|
||
sprintf(sendbuf, "EXECUTE %ld %s", pc->rcvbufsize, command);
|
||
remote_tcp_write_string(pc->sockfd, sendbuf);
|
||
|
||
bzero(readbuf, READBUFSIZE);
|
||
done = total = 0;
|
||
dtot = 0;
|
||
|
||
while (!done) {
|
||
|
||
req = pc->rcvbufsize;
|
||
bufptr = readbuf;
|
||
tot = 0;
|
||
|
||
while (req) {
|
||
ret = recv(pc->sockfd, bufptr, req, 0);
|
||
if (!tot) {
|
||
if (STRNEQ(bufptr, FAILMSG)) {
|
||
fprintf(fp,
|
||
"remote_execute: %s\n",
|
||
bufptr);
|
||
tot = -1;
|
||
break;
|
||
}
|
||
if (STRNEQ(bufptr, DONEMSG) ||
|
||
STRNEQ(bufptr, DATAMSG)) {
|
||
BCOPY(bufptr, datahdr, DATA_HDRSIZE);
|
||
if (CRASHDEBUG(1))
|
||
fprintf(fp,
|
||
"remote_execute: [%s]\n",
|
||
datahdr);
|
||
p1 = strtok(datahdr, " "); /* DONE */
|
||
if (STREQ(p1, "DONE"))
|
||
done = TRUE;
|
||
p1 = strtok(NULL, " "); /* count */
|
||
dtot = atol(p1);
|
||
total += dtot;
|
||
}
|
||
}
|
||
req -= ret;
|
||
tot += ret;
|
||
bufptr += ret;
|
||
}
|
||
|
||
if (tot == -1)
|
||
break;
|
||
|
||
if (fwrite(&readbuf[DATA_HDRSIZE], sizeof(char), dtot, fp)
|
||
!= dtot)
|
||
error(FATAL, "fwrite failed\n");
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
/*
|
||
* Clean up on exit.
|
||
*/
|
||
void
|
||
remote_exit(void)
|
||
{
|
||
char buf[BUFSIZE];
|
||
|
||
if (pc->flags & UNLINK_NAMELIST)
|
||
unlink(pc->namelist);
|
||
|
||
if (pc->flags & UNLINK_MODULES)
|
||
unlink_module(NULL);
|
||
|
||
BZERO(buf, BUFSIZE);
|
||
sprintf(buf, "EXIT");
|
||
remote_tcp_write_string(pc->sockfd, buf);
|
||
/*
|
||
* Read but ignore the return status -- we don't really care...
|
||
*/
|
||
remote_tcp_read_string(pc->sockfd, buf, BUFSIZE-1, NIL_MODE());
|
||
|
||
}
|
||
#endif /* !DAEMON */
|