#include <glib.h>
#include <string.h>
#include <stdlib.h>
#include <syslog.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/param.h>
#include <sys/mount.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include "netconsole.h"
#include "netdumpclient.h"
#include "netdumpelf.h"
#include "server.h"
#define MAX_N_RETRIES 20
#define NETDUMP_INITIAL_TIMEOUT_MSEC 100
#define NETDUMP_TIMEOUT_MULTIPLIER 1.5
#define NETDUMP_MAX_TIMEOUT_MSEC 4000
#define NETDUMP_OUTSTANDING_PACKETS 10
#define NETDUMP_ACKWAIT_MSEC 50
/* How many reboot requests to send */
#define NETDUMP_N_REBOOT_REQUESTS 20
/* The timeout between the reboot requests */
#define NETDUMP_REBOOT_DELAY_MSEC 500
#undef DEBUG
static void netdump_client_remove_timeout (NetdumpClient *client);
static gboolean generate_i386_elf_header (NetdumpClient *client);
static gboolean generate_x86_64_elf_header (NetdumpClient *client);
static gboolean generate_ppc64_elf_header (NetdumpClient *client);
static gboolean generate_ia64_elf_header (NetdumpClient *client);
static void handshake (NetdumpClient *client);
static void handshake_send_hello (NetdumpClient *client,
guint32 request);
static void handshake_packet (NetdumpClient *client,
struct sockaddr_in *addr,
unsigned char *buffer,
int buf_len);
static void handshake_timeout (NetdumpClient *client,
guint32 request);
static void nr_pages_request (NetdumpClient *client);
static void nr_pages_packet (NetdumpClient *client,
struct sockaddr_in *addr,
unsigned char *buffer,
int buf_len);
static void nr_pages_timeout (NetdumpClient *client,
guint32 request);
static void page_size_request (NetdumpClient *client);
static void page_size_packet (NetdumpClient *client,
struct sockaddr_in *addr,
unsigned char *buffer,
int buf_len);
static void page_size_timeout (NetdumpClient *client,
guint32 request);
static void regs_request (NetdumpClient *client);
static void regs_packet (NetdumpClient *client,
struct sockaddr_in *addr,
unsigned char *buffer,
int buf_len);
static void regs_timeout (NetdumpClient *client,
guint32 request);
static void status_request (NetdumpClient *client,
gboolean first);
static void status_packet (NetdumpClient *client,
struct sockaddr_in *addr,
unsigned char *buffer,
int buf_len);
static void status_timeout (NetdumpClient *client,
guint32 request);
static void memory_request (NetdumpClient *client,
gboolean first_time);
static void memory_timeout (NetdumpClient *client,
guint32 request);
static void memory_packet (NetdumpClient *client,
struct sockaddr_in *addr,
unsigned char *buffer,
int buf_len);
void netdump_client_send_request (NetdumpClient *client,
enum netdump_commands command,
guint32 from,
guint32 to,
gboolean setup_timeout,
guint *timeout_delay);
typedef struct {
guint32 ip;
int count;
unsigned char magic_value[MAGIC_SIZE];
} RebootRequest;
static gboolean
netdump_client_send_reboot (gpointer data)
{
req_t req;
struct sockaddr_in addr;
int addr_len = sizeof(addr);
int len;
RebootRequest *reboot = data;
#if 0
g_print ("sending reboot to %d.%d.%d.%d\n",
(client->ip >> 24) & 0xff,
(client->ip >> 16) & 0xff,
(client->ip >> 8) & 0xff,
(client->ip >> 0) & 0xff);
#endif
memset (&addr, 0, sizeof (addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(NETDUMP_PORT);
addr.sin_addr.s_addr = htonl(reboot->ip);
memset (&req, 0, sizeof(req));
memcpy ((char *)&req.magic, reboot->magic_value, MAGIC_SIZE);
req.nr = htonl (0);
req.command = htonl (COMM_REBOOT);
req.from = htonl(0);
req.to = htonl(0);
len = sendto (master_socket,
&req, sizeof(req), 0,
(struct sockaddr *)&addr, addr_len);
reboot->count++;
if (reboot->count < NETDUMP_N_REBOOT_REQUESTS)
{
g_timeout_add_full (G_PRIORITY_DEFAULT,
NETDUMP_REBOOT_DELAY_MSEC,
netdump_client_send_reboot,
reboot,
NULL);
}
else
g_free (reboot);
return FALSE;
}
static void
netdump_client_reboot (NetdumpClient *client)
{
RebootRequest *reboot;
execute_script ("netdump-reboot", client->ip, client->dump_dir);
reboot = g_new0 (RebootRequest, 1);
reboot->ip = client->ip;
memcpy (reboot->magic_value, client->magic_value, MAGIC_SIZE);
reboot->count = 0;
(void)netdump_client_send_reboot (reboot);
}
void
netdump_client_destroy (NetdumpClient *client, gboolean reboot)
{
int i;
#ifdef DEBUG
g_print (" netdump_client_destroy(%p, reboot=%d)\n", client, reboot);
#endif
remove_client (client);
netdump_client_remove_timeout (client);
if (reboot)
netdump_client_reboot (client);
if (client->dump_fd >= 0)
close (client->dump_fd);
for (i = 0; i < client->n_outstanding; i++)
{
if (client->outstanding[i].timeout_tag != 0)
g_source_remove (client->outstanding[i].timeout_tag);
g_free (client->outstanding[i].data);
}
g_free (client->outstanding);
g_free (client->regs);
g_free (client->dump_dir);
g_free (client);
}
guint
netdump_client_hash (gconstpointer key)
{
const NetdumpClient *client = key;
return client->ip;
}
gint
netdump_client_compare (gconstpointer a,
gconstpointer b)
{
const NetdumpClient *client1 = a;
const NetdumpClient *client2 = b;
return (client1->ip == client2->ip);
}
struct TimeoutData {
NetdumpClient *client;
guint32 request_nr;
};
static gboolean
netdump_client_timeout (gpointer data)
{
struct TimeoutData *timeout = data;
if (timeout->client->timeout)
timeout->client->timeout (timeout->client, timeout->request_nr);
return FALSE;
}
static void
netdump_client_remove_timeout (NetdumpClient *client)
{
if (client->timeout_tag)
g_source_remove (client->timeout_tag);
client->timeout_tag = 0;
}
void
netdump_client_setup_timeout (NetdumpClient *client,
guint32 request_nr,
guint timeout_delay)
{
struct TimeoutData *timeout;
timeout = g_new (struct TimeoutData, 1);
timeout->client = client;
timeout->request_nr = request_nr;
client->timeout_tag =
g_timeout_add_full (G_PRIORITY_DEFAULT,
timeout_delay,
netdump_client_timeout,
timeout,
g_free);
}
void
netdump_client_send_request (NetdumpClient *client,
enum netdump_commands command,
guint32 from,
guint32 to,
gboolean setup_timeout,
guint *timeout_delay)
{
req_t req;
struct sockaddr_in addr;
int addr_len = sizeof(addr);
int len;
#if 0
g_print ("sending command %d to %d.%d.%d.%d\n",
command,
(client->ip >> 24) & 0xff,
(client->ip >> 16) & 0xff,
(client->ip >> 8) & 0xff,
(client->ip >> 0) & 0xff);
#endif
memset (&addr, 0, sizeof (addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(NETDUMP_PORT);
addr.sin_addr.s_addr = htonl(client->ip);
memset (&req, 0, sizeof(req));
/*
* This is "legacy" code that is completely useless.
* Notwithstanding any endian issue with the memcpy,
* the client never has done anything with the magic
* value.
*/
//memcpy ((char *)&req.magic, client->magic_value, MAGIC_SIZE);
req.nr = htonl (++client->outstanding_request);
req.command = htonl (command);
req.from = htonl(from);
req.to = htonl(to);
len = sendto (master_socket,
&req, sizeof(req), 0,
(struct sockaddr *)&addr, addr_len);
if (setup_timeout)
{
if (*timeout_delay == 0)
*timeout_delay = NETDUMP_INITIAL_TIMEOUT_MSEC/2 + (rand() % (NETDUMP_INITIAL_TIMEOUT_MSEC/2));
else
*timeout_delay = MIN (*timeout_delay * NETDUMP_TIMEOUT_MULTIPLIER, NETDUMP_MAX_TIMEOUT_MSEC);
netdump_client_setup_timeout (client,
client->outstanding_request,
*timeout_delay);
}
}
NetdumpClient *
netdump_client_new (guint32 ip,
const char *dir,
const char magic[MAGIC_SIZE],
gboolean just_reboot,
reply_t *reply,
char *msg)
{
NetdumpClient *client;
reply->nr = ntohl (reply->nr);
reply->info = ntohl (reply->info);
client = g_new0 (NetdumpClient, 1);
#ifdef DEBUG
g_print (" netdump_client_new() ip: %d.%d.%d.%d -> %p\n",
(ip >> 24) & 0xff,
(ip >> 16) & 0xff,
(ip >> 8) & 0xff,
(ip) & 0xff,
client);
#endif
client->ip = ip;
sprintf(client->ip_addr, "%d.%d.%d.%d",
(ip >> 24) & 0xff,
(ip >> 16) & 0xff,
(ip >> 8) & 0xff,
(ip) & 0xff);
client->dump_dir = g_strdup (dir);
client->just_reboot = just_reboot;
memcpy (client->magic_value, magic, MAGIC_SIZE);
client->dump_fd = -1;
client->outstanding_request = 0;
/* Lame default. should be read from kernel */
client->page_offset = 0xC0000000;
/*
* Handshake protocol requires that we operate at the
* lowest common denominator of the client and server
* version numbers.
*
* The original "base", i386-only, netdump client did not
* use (set to 0) the reply->info and reply->nr fields
* of this REPLY_START_NETDUMP packet. Subsequent clients
* put their real version value in the reply->info field,
* and their machine type in reply->nr.
*
* The original "base", i386-only, netdump server put a
* value of 0 in the req->from value that gets sent back
* in the handshake() COMM_START_NETDUMP_ACK packet.
* Subsequent servers put their server version in the
* req->from field, and newer clients will adjust their
* "effective" version accordingly.
*/
if (reply->info == 0)
client->effective_version = NETDUMP_SERVER_VERSION_BASE;
else if (reply->info > NETDUMP_SERVER_VERSION_CURRENT)
client->effective_version = NETDUMP_SERVER_VERSION_CURRENT;
else
client->effective_version = reply->info;
switch (reply->nr)
{
case 0:
case EM_386:
client->machine_type = EM_386;
break;
case EM_X86_64:
client->machine_type = EM_X86_64;
break;
case EM_PPC64:
client->machine_type = EM_PPC64;
break;
case EM_IA_64:
client->machine_type = EM_IA_64;
break;
default:
client->just_reboot = TRUE;
break;
}
/*
* Post-"base" clients pass information in the appended
* message string. Parse the string for expected info,
* which comes from the client in this format:
*
* "item1:value item2:value ... itemX:value\n"
*/
if (client->effective_version > NETDUMP_SERVER_VERSION_BASE) {
char *p;
if ((p = strstr(msg, "task_struct:0x")))
client->task_struct = strtoull(strstr(p, "0x"), NULL, 16);
if ((p = strstr(msg, "page_offset:0x")))
client->page_offset = strtoull(strstr(p, "0x"), NULL, 16);
if ((p = strstr(msg, "netdump_magic:0x"))) {
client->netdump_magic = strtoull(strstr(p, "0x"), NULL, 16);
if (!verify_magic(client))
client->just_reboot = TRUE;
}
}
client->n_timeouts = 0;
client->n_handshakes = 0;
client->timeout_delay = 0;
handshake (client);
return client;
}
static gboolean
parse_packet (unsigned char *buffer,
int buf_len,
reply_t *reply)
{
if (buf_len < 1 + sizeof (reply_t) ||
!VERSION_VERIFIED(buffer[0]))
return TRUE;
memcpy ((char *)reply, buffer + 1, sizeof(reply_t));
reply->nr = ntohl (reply->nr);
reply->code = ntohl (reply->code);
reply->info = ntohl (reply->info);
#if 0
g_print ("Parsed reply: nr: %d, code: %d, info: %d\n",
reply->nr, reply->code, reply->info);
g_print ("packet data: '%s'\n", buffer + 1 + sizeof (reply_t));
#endif
return FALSE;
}
/********************************************************************************
* Handshaking *
********************************************************************************/
static void
handshake (NetdumpClient *client)
{
struct TimeoutData *timeout;
#ifdef DEBUG
g_print ("handshake(%p)\n", client);
#endif
netdump_client_send_request (client,
COMM_START_NETDUMP_ACK,
NETDUMP_SERVER_VERSION_CURRENT,
0, FALSE, NULL);
/* Wait a while for the ACK to arrive at the client */
timeout = g_new (struct TimeoutData, 1);
timeout->client = client;
timeout->request_nr = 0;
client->timeout_tag =
g_timeout_add_full (G_PRIORITY_DEFAULT,
NETDUMP_ACKWAIT_MSEC,
netdump_client_timeout,
timeout,
g_free);
client->process_packet = NULL;
client->timeout = handshake_send_hello;
}
static void
handshake_send_hello (NetdumpClient *client,
guint32 request)
{
#ifdef DEBUG
g_print ("handshake_send_hello(%p)\n", client);
#endif
netdump_client_send_request (client,
COMM_HELLO,
0, 0, TRUE,
&client->timeout_delay);
client->process_packet = handshake_packet;
client->timeout = handshake_timeout;
}
static void
handshake_packet (NetdumpClient *client,
struct sockaddr_in *addr,
unsigned char *buffer,
int buf_len)
{
reply_t reply;
#ifdef DEBUG
g_print ("handshake_packet(%p)\n", client);
#endif
if (parse_packet (buffer, buf_len, &reply))
{
syslog (LOG_WARNING, "Got invalid packet from %s\n", client->ip_addr);
return;
}
/* If out of order reply, ignore */
if (reply.nr != client->outstanding_request)
return;
/* If strange reply, ignore. The timeout will catch this. */
if (reply.code != REPLY_HELLO)
return;
netdump_client_remove_timeout (client);
/* Now request the page size */
client->n_timeouts = 0;
if (client->just_reboot)
{
status_request (client, TRUE);
}
else
{
/* Now request the page size */
client->n_timeouts = 0;
client->timeout_delay = 0;
nr_pages_request (client);
}
}
static void
handshake_timeout (NetdumpClient *client,
guint32 request)
{
client->n_timeouts++;
#ifdef DEBUG
g_print ("handshake_timeout(%p)\n", client);
#endif
if (client->n_timeouts < MAX_N_RETRIES)
handshake_send_hello (client, 0);
else
{
client->n_handshakes++;
if (client->n_handshakes < 10)
{
/* Restart exponential backoff */
client->timeout_delay = 0;
handshake (client);
}
else
{
syslog (LOG_WARNING, "Got too many timeouts in handshaking, ignoring client %s\n", client->ip_addr);
status_request (client, TRUE);
}
}
}
/********************************************************************************
* NR_PAGES *
********************************************************************************/
static void
nr_pages_request (NetdumpClient *client)
{
#ifdef DEBUG
g_print ("nr_pages_request (%p)\n", client);
#endif
netdump_client_send_request (client,
COMM_GET_NR_PAGES,
0, 0, TRUE,
&client->timeout_delay);
client->process_packet = nr_pages_packet;
client->timeout = nr_pages_timeout;
}
static void
nr_pages_packet (NetdumpClient *client,
struct sockaddr_in *addr,
unsigned char *buffer,
int buf_len)
{
reply_t reply;
if (parse_packet (buffer, buf_len, &reply))
{
syslog (LOG_WARNING, "Got invalid packet from %s\n", client->ip_addr);
return;
}
/* If out of order reply, ignore */
if (reply.nr != client->outstanding_request)
return;
/* If strange reply, ignore. The timeout will catch this. */
if (reply.code != REPLY_NR_PAGES)
return;
client->nr_pages = reply.info;
netdump_client_remove_timeout (client);
/* Now request the page size */
client->n_timeouts = 0;
client->timeout_delay = 0;
page_size_request (client);
}
static void
nr_pages_timeout (NetdumpClient *client,
guint32 request)
{
client->n_timeouts++;
if (client->n_timeouts < MAX_N_RETRIES)
nr_pages_request (client);
else
{
syslog (LOG_WARNING, "Got too many timeouts waiting for NR_PAGES for client %s, ignoring it\n", client->ip_addr);
status_request (client, TRUE);
}
}
/********************************************************************************
* PAGE_SIZE *
********************************************************************************/
static void
page_size_request (NetdumpClient *client)
{
#ifdef DEBUG
g_print ("page_size_request (%p)\n", client);
#endif
netdump_client_send_request (client,
COMM_GET_PAGE_SIZE,
0, 0, TRUE,
&client->timeout_delay);
client->process_packet = page_size_packet;
client->timeout = page_size_timeout;
}
static void
page_size_packet (NetdumpClient *client,
struct sockaddr_in *addr,
unsigned char *buffer,
int buf_len)
{
reply_t reply;
if (parse_packet (buffer, buf_len, &reply))
{
syslog (LOG_WARNING, "Got invalid packet from %s\n", client->ip_addr);
return;
}
/* If out of order reply, ignore */
if (reply.nr != client->outstanding_request)
return;
/* If strange reply, ignore. The timeout will catch this. */
if (reply.code != REPLY_PAGE_SIZE)
return;
client->page_size = reply.info;
netdump_client_remove_timeout (client);
/* Now request memory... */
client->n_timeouts = 0;
client->timeout_delay = 0;
regs_request (client);
}
static void
page_size_timeout (NetdumpClient *client,
guint32 request)
{
client->n_timeouts++;
if (client->n_timeouts < MAX_N_RETRIES)
page_size_request (client);
else
{
syslog (LOG_WARNING, "Got too many timeouts waiting for PAGE_SIZE for client %s, ignoring it\n", client->ip_addr);
status_request (client, TRUE);
}
}
/********************************************************************************
* Registers *
********************************************************************************/
static void
regs_request (NetdumpClient *client)
{
#ifdef DEBUG
g_print ("regs_request (%p)\n", client);
#endif
netdump_client_send_request (client,
COMM_GET_REGS,
0, 0, TRUE,
&client->timeout_delay);
client->process_packet = regs_packet;
client->timeout = regs_timeout;
}
static void
regs_packet (NetdumpClient *client,
struct sockaddr_in *addr,
unsigned char *buffer,
int buf_len)
{
reply_t reply;
int offset;
if (parse_packet (buffer, buf_len, &reply))
{
syslog (LOG_WARNING, "Got invalid packet from %s\n", client->ip_addr);
return;
}
/* If out of order reply, ignore */
if (reply.nr != client->outstanding_request)
return;
/* If strange reply, ignore. The timeout will catch this. */
if (reply.code != REPLY_REGS)
return;
/*
* The base client version incorrectly sent its max_mapnr
* in the reply->info field, and the base server incorrectly
* put it in client->regs_len, which then gets recalculated
* below. A new field, client->totalram_pages, has been
* added to hold the kernel's totalram_pages value, and
* is used by the /var/crash filesystem space check.
*/
switch (client->effective_version)
{
case NETDUMP_SERVER_VERSION_BASE:
client->totalram_pages = client->nr_pages;
break;
default:
client->totalram_pages = reply.info;
break;
}
/* Find end of ascii data, where regs data starts */
offset = 1 + sizeof (reply_t);
while (offset < buf_len && buffer[offset] != '\n')
offset++;
if (offset < buf_len)
offset++; /* Skip past the \n */
if (offset < buf_len)
{
client->regs_len = buf_len - offset;
client->regs = g_malloc (client->regs_len);
memcpy (client->regs, buffer + offset, client->regs_len);
}
netdump_client_remove_timeout (client);
/* Now request memory... */
client->n_timeouts = 0;
client->timeout_delay = 0;
memory_request (client, TRUE);
}
static void
regs_timeout (NetdumpClient *client,
guint32 request)
{
client->n_timeouts++;
if (client->n_timeouts < MAX_N_RETRIES)
regs_request (client);
else
{
syslog (LOG_WARNING, "Got too many timeouts waiting for REGS for client %s, ignoring it\n", client->ip_addr);
status_request (client, TRUE);
}
}
/********************************************************************************
* Dump memory *
********************************************************************************/
static void
get_page (NetdumpClient *client, OutstandingPage *outstanding, guint32 page_nr)
{
outstanding->page_nr = page_nr;
netdump_client_send_request (client,
COMM_SEND_MEM,
outstanding->page_nr, 0, TRUE,
&outstanding->timeout_delay);
#ifdef DEBUG
g_print ("Outstanding %d: sendig SEND_MEM request for page %d, timeout: %d\n",
outstanding - client->outstanding, page_nr, outstanding->timeout_delay);
#endif
outstanding->request_nr = client->outstanding_request;
outstanding->timeout_tag = client->timeout_tag;
}
static void
memory_request (NetdumpClient *client, gboolean first_time)
{
int i;
gboolean has_active;
OutstandingPage *outstanding;
gboolean tried_once = FALSE;
if (first_time)
{
char *file;
struct statfs buf;
retry:
file = g_strconcat (client->dump_dir, "/vmcore-incomplete", NULL);
client->dump_fd = open (file, O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR);
if (client->dump_fd < 0)
{
syslog (LOG_ERR, "unable to open crash dump file");
status_request (client, TRUE);
g_free (file);
return;
}
if (fstatfs(client->dump_fd, &buf) < 0)
syslog (LOG_WARNING, "unable to statfs on the crash dump file");
else if (!perform_space_check())
syslog (LOG_NOTICE, "crash dump file space check not performed");
else
{
off_t memdump_size;
int res;
memdump_size = ((off_t)client->totalram_pages * (off_t)client->page_size + buf.f_bsize - 1) / buf.f_bsize;
if (memdump_size > buf.f_bavail)
{
if (!tried_once)
{
res = execute_script ("netdump-nospace", client->ip, client->dump_dir);
if (res > 0)
{
close (client->dump_fd);
client->dump_fd = -1;
unlink (file);
g_free (file);
tried_once = TRUE;
goto retry;
}
}
if (tried_once)
syslog (LOG_ERR, "No space for dump image, even after calling netdump-nospace");
else
syslog (LOG_ERR, "No space for dump image, avail = %lld, need = %lld\n", buf.f_bavail, memdump_size);
status_request (client, TRUE);
g_free (file);
return;
}
}
g_free (file);
switch (client->machine_type)
{
case EM_386:
if (generate_i386_elf_header (client))
{
syslog (LOG_WARNING, "unable to dump i386 elf header");
status_request (client, TRUE);
return;
}
break;
case EM_X86_64:
if (generate_x86_64_elf_header (client))
{
syslog (LOG_WARNING, "unable to dump x86_64 elf header");
status_request (client, TRUE);
return;
}
break;
case EM_PPC64:
if (generate_ppc64_elf_header (client))
{
syslog (LOG_WARNING, "unable to dump ppc64 elf header");
status_request (client, TRUE);
return;
}
break;
case EM_IA_64:
if (generate_ia64_elf_header (client))
{
syslog (LOG_WARNING, "unable to dump ia64 elf header");
status_request (client, TRUE);
return;
}
break;
}
client->current_page = 0;
client->next_avail_page = 0;
client->n_outstanding = NETDUMP_OUTSTANDING_PACKETS;
client->outstanding = g_new (OutstandingPage, client->n_outstanding);
for (i = 0; i < client->n_outstanding; i++)
{
client->outstanding[i].request_nr = 0;
client->outstanding[i].timeout_tag = 0;
client->outstanding[i].timeout_delay = 0;
client->outstanding[i].data = g_malloc (client->page_size);
}
}
client->process_packet = memory_packet;
client->timeout = memory_timeout;
has_active = FALSE;
for (i = 0; i < client->n_outstanding; i++)
{
if (client->outstanding[i].request_nr != 0)
has_active = TRUE;
else if (client->current_page != client->nr_pages)
{
outstanding = &client->outstanding[i];
outstanding->n_timeouts = 0;
outstanding->sections_received = 0;
outstanding->timeout_delay = 0;
/*
* The client->next_avail_page field may have been
* set non-zero in memory_packet() upon receipt of
* a REPLY_RESERVED packet containing a non-zero
* reply->info field; the client has indicated that
* the memory request was to a non-RAM page, and that
* reply->info contains the next legitimate page number.
*/
if (client->next_avail_page > client->current_page)
{
if (client->next_avail_page >= client->nr_pages)
{
client->current_page = client->nr_pages;
continue;
}
client->current_page = client->next_avail_page;
}
get_page (client, outstanding, client->current_page++);
has_active = TRUE;
}
}
/* Did we finish dumping all memory */
if ((client->current_page == client->nr_pages) && !has_active)
{
char *old_file;
char *new_file;
old_file = g_strconcat (client->dump_dir, "/vmcore-incomplete", NULL);
new_file = g_strconcat (client->dump_dir, "/vmcore", NULL);
if (link(old_file, new_file) == 0)
unlink (old_file);
g_free (old_file);
g_free (new_file);
/* Request the client status */
status_request (client, TRUE);
}
}
static OutstandingPage *
find_outstanding (NetdumpClient *client,
guint32 request)
{
OutstandingPage *outstanding = NULL;
int i;
for (i = 0; i < client->n_outstanding; i++)
if (client->outstanding[i].request_nr == request)
{
outstanding = &client->outstanding[i];
break;
}
return outstanding;
}
static void
memory_remove_outstanding_timeouts (NetdumpClient *client)
{
OutstandingPage *outstanding = NULL;
int i;
for (i = 0; i < client->n_outstanding; i++) {
outstanding = &client->outstanding[i];
if (outstanding->timeout_tag != 0)
g_source_remove (outstanding->timeout_tag);
outstanding->timeout_tag = 0;
}
return;
}
static void
memory_timeout (NetdumpClient *client,
guint32 request)
{
OutstandingPage *outstanding;
outstanding = find_outstanding (client, request);
if (outstanding == NULL)
{
syslog (LOG_WARNING, "Got bogus (non-expected) GET_PAGE timeout (request nr: %d)\n", request);
return;
}
outstanding->n_timeouts++;
outstanding->timeout_tag = 0;
#ifdef DEBUG
g_print ("Got timeout for Outstanding %d: It now has gotten %d timeouts\n",
outstanding - client->outstanding, outstanding->n_timeouts);
#endif
if (outstanding->n_timeouts < MAX_N_RETRIES)
{
get_page (client, outstanding, outstanding->page_nr);
}
else
{
syslog (LOG_WARNING, "Got too many timeouts waiting for memory page for client %s, ignoring it\n", client->ip_addr);
memory_remove_outstanding_timeouts (client);
status_request (client, TRUE);
}
}
static void
memory_packet (NetdumpClient *client,
struct sockaddr_in *addr,
unsigned char *buffer,
int buf_len)
{
OutstandingPage *outstanding;
reply_t reply;
gboolean done;
int offset;
if (parse_packet (buffer, buf_len, &reply))
{
syslog (LOG_WARNING, "Got invalid packet from %s\n", client->ip_addr);
return;
}
outstanding = find_outstanding (client, reply.nr);
/* If out of order reply, ignore */
if (outstanding == NULL)
return;
/* If strange reply, ignore. The timeout will catch this. */
if (reply.code != REPLY_RESERVED &&
reply.code != REPLY_MEM)
return;
done = FALSE;
if (reply.code == REPLY_MEM)
{
offset = reply.info;
#ifdef DEBUG
g_print ("Got packet for Outstanding %d: offset=%d\n",
outstanding - client->outstanding, offset);
#endif
outstanding->sections_received |= 1 << (offset/1024);
if ((offset >= 0) && (offset + 1024 <= client->page_size))
memcpy (outstanding->data + offset,
buffer + 1 + sizeof (reply_t),
1024);
/* Check if we have got all page sections */
if (outstanding->sections_received ==
((1 << client->page_size/1024) - 1))
{
int left, len;
done = TRUE;
lseek (client->dump_fd,
(off_t)client->data_offset + (off_t)outstanding->page_nr * (off_t)client->page_size,
SEEK_SET);
left = client->page_size;
while (left > 0)
{
len = write (client->dump_fd,
outstanding->data + (client->page_size - left),
left);
if (len < 0)
{
syslog (LOG_ERR, "Error writing memory dump\n");
memory_remove_outstanding_timeouts (client);
status_request (client, TRUE);
return;
}
left -= len;
}
}
}
else
{
/*
* REPLY_RESERVED was never implemented in the base client.
* Instead, non-RAM pages were replaced by a mapping of
* the kernel's ZERO_PAGE.
*
* Post-base clients use REPLY_RESERVED to avoid the unnecessary
* and wasteful usage of zero-filled pages. Furthermore, if the
* reply->info field is non-zero, the client is indicating that
* it's safe to skip to that next available page.
*/
done = TRUE;
if (reply.info > client->next_avail_page)
client->next_avail_page = reply.info;
}
if (done)
{
#ifdef DEBUG
g_print ("Got all packets packet for Outstanding %d: requesting new page\n",
outstanding - client->outstanding);
#endif
outstanding->request_nr = 0;
if (outstanding->timeout_tag)
g_source_remove (outstanding->timeout_tag);
outstanding->timeout_tag = 0;
outstanding->timeout_delay = 0;
memory_request (client, FALSE);
}
}
/********************************************************************************
* Elf core dumping *
********************************************************************************/
u16
swab16(u16 x)
{
return (((x & 0x00ff) << 8) |
((x & 0xff00) >> 8));
}
u32
swab32(u32 x)
{
return (((x & 0x000000ffU) << 24) |
((x & 0x0000ff00U) << 8) |
((x & 0x00ff0000U) >> 8) |
((x & 0xff000000U) >> 24));
}
u64
swab64(u64 x)
{
return (((x & 0x00000000000000ffULL) << 56) |
((x & 0x000000000000ff00ULL) << 40) |
((x & 0x0000000000ff0000ULL) << 24) |
((x & 0x00000000ff000000ULL) << 8) |
((x & 0x000000ff00000000ULL) >> 8) |
((x & 0x0000ff0000000000ULL) >> 24) |
((x & 0x00ff000000000000ULL) >> 40) |
((x & 0xff00000000000000ULL) >> 56));
}
void
Elf32_Ehdr_swab(NetdumpClient *client, Elf32_Ehdr *ehdr)
{
ehdr->e_type = swab16(ehdr->e_type);
ehdr->e_machine = swab16(ehdr->e_machine);
ehdr->e_version = swab32(ehdr->e_version);
ehdr->e_entry = swab32(ehdr->e_entry);
ehdr->e_phoff = swab32(ehdr->e_phoff);
ehdr->e_shoff = swab32(ehdr->e_shoff);
ehdr->e_flags = swab32(ehdr->e_flags);
ehdr->e_ehsize = swab16(ehdr->e_ehsize);
ehdr->e_phentsize = swab16(ehdr->e_phentsize);
ehdr->e_phnum = swab16(ehdr->e_phnum);
ehdr->e_shentsize = swab16(ehdr->e_shentsize);
ehdr->e_shnum = swab16(ehdr->e_shnum);
ehdr->e_shstrndx = swab16(ehdr->e_shstrndx);
}
void
Elf32_Phdr_swab(NetdumpClient *client, Elf32_Phdr *phdr)
{
phdr->p_type = swab32(phdr->p_type);
phdr->p_offset = swab32(phdr->p_offset);
phdr->p_vaddr = swab32(phdr->p_vaddr);
phdr->p_paddr = swab32(phdr->p_paddr);
phdr->p_filesz = swab32(phdr->p_filesz);
phdr->p_memsz = swab32(phdr->p_memsz);
phdr->p_flags = swab32(phdr->p_flags);
phdr->p_align = swab32(phdr->p_align);
}
void
Elf32_Nhdr_swab(NetdumpClient *client, Elf32_Nhdr *nhdr)
{
nhdr->n_namesz = swab32(nhdr->n_namesz);
nhdr->n_descsz = swab32(nhdr->n_descsz);
nhdr->n_type = swab32(nhdr->n_type);
}
void
Elf64_Ehdr_swab(NetdumpClient *client, Elf64_Ehdr *ehdr)
{
ehdr->e_type = swab16(ehdr->e_type);
ehdr->e_machine = swab16(ehdr->e_machine);
ehdr->e_version = swab32(ehdr->e_version);
ehdr->e_entry = swab64(ehdr->e_entry);
ehdr->e_phoff = swab64(ehdr->e_phoff);
ehdr->e_shoff = swab64(ehdr->e_shoff);
ehdr->e_flags = swab32(ehdr->e_flags);
ehdr->e_ehsize = swab16(ehdr->e_ehsize);
ehdr->e_phentsize = swab16(ehdr->e_phentsize);
ehdr->e_phnum = swab16(ehdr->e_phnum);
ehdr->e_shentsize = swab16(ehdr->e_shentsize);
ehdr->e_shnum = swab16(ehdr->e_shnum);
ehdr->e_shstrndx = swab16(ehdr->e_shstrndx);
}
void
Elf64_Phdr_swab(NetdumpClient *client, Elf64_Phdr *phdr)
{
phdr->p_type = swab32(phdr->p_type);
phdr->p_flags = swab32(phdr->p_flags);
phdr->p_offset = swab64(phdr->p_offset);
phdr->p_vaddr = swab64(phdr->p_vaddr);
phdr->p_paddr = swab64(phdr->p_paddr);
phdr->p_filesz = swab64(phdr->p_filesz);
phdr->p_memsz = swab64(phdr->p_memsz);
phdr->p_align = swab64(phdr->p_align);
}
void
Elf64_Nhdr_swab(NetdumpClient *client, Elf64_Nhdr *nhdr)
{
nhdr->n_namesz = swab32(nhdr->n_namesz);
nhdr->n_descsz = swab32(nhdr->n_descsz);
nhdr->n_type = swab32(nhdr->n_type);
}
#define roundup(x, y) ((((x)+((y)-1))/(y))*(y))
static size_t
dump_elf32_note (NetdumpClient *client,
char *buf,
Elf32_Word type,
char *name,
char *desc, int d_len)
{
Elf32_Nhdr *note;
size_t len;
note = (Elf32_Nhdr *)buf;
note->n_namesz = strlen (name);
note->n_descsz = d_len;
note->n_type = type;
len = sizeof (Elf32_Nhdr);
memcpy (buf + len, name, note->n_namesz);
len = roundup (len + note->n_namesz, 4);
memcpy (buf + len, desc, note->n_descsz);
len = roundup (len + note->n_descsz, 4);
if (BYTE_SWAP_REQUIRED(client))
Elf32_Nhdr_swab(client, note);
return len;
}
static gboolean
generate_i386_elf_header (NetdumpClient *client)
{
char *buffer, *ptr;
Elf32_Ehdr *elf;
Elf32_Phdr *notes;
Elf32_Phdr *load;
size_t offset, len;
struct elf_prpsinfo_32 prpsinfo;
struct elf_prstatus_i386 prstatus;
u32 task_struct;
buffer = g_malloc0 (sizeof (Elf32_Ehdr) + 2*sizeof (Elf32_Phdr) +
4096*2); /* 4096*2 should be enough for the notes + roundup */
offset = 0;
ptr = buffer;
/* Elf header */
elf = (Elf32_Ehdr *)ptr;
memcpy (elf->e_ident, ELFMAG, SELFMAG);
elf->e_ident[EI_CLASS] = ELFCLASS32;
elf->e_ident[EI_DATA] = ELFDATA2LSB;
elf->e_ident[EI_VERSION]= EV_CURRENT;
memset (elf->e_ident+EI_PAD, 0, EI_NIDENT-EI_PAD);
elf->e_type = ET_CORE;
elf->e_machine = EM_386;
elf->e_version = EV_CURRENT;
elf->e_entry = 0;
elf->e_phoff = sizeof (Elf32_Ehdr);
elf->e_shoff = 0;
elf->e_flags = 0;
elf->e_ehsize = sizeof (Elf32_Ehdr);
elf->e_phentsize = sizeof (Elf32_Phdr);
elf->e_phnum = 2;
elf->e_shentsize = 0;
elf->e_shnum = 0;
elf->e_shstrndx = 0;
offset += sizeof (Elf32_Ehdr);
ptr += sizeof (Elf32_Ehdr);
/* PT_NOTE */
notes = (Elf32_Phdr *)ptr;
notes->p_type = PT_NOTE;
notes->p_offset = 0; /* TO BE FILLED IN */
notes->p_vaddr = 0;
notes->p_paddr = 0;
notes->p_filesz = 0; /* TO BE FILLED IN */
notes->p_memsz = 0;
notes->p_flags = 0;
notes->p_align = 0;
offset += sizeof (Elf32_Phdr);
ptr += sizeof (Elf32_Phdr);
/* PT_LOAD */
load = (Elf32_Phdr *)ptr;
load->p_type = PT_LOAD;
load->p_offset = 0; /* TO BE FILLED IN */
load->p_vaddr = (Elf32_Addr)client->page_offset;
load->p_paddr = 0;
load->p_filesz = client->page_size * client->nr_pages;
load->p_memsz = load->p_filesz;
load->p_flags = PF_R|PF_W|PF_X;
load->p_align = client->page_size;
offset += sizeof (Elf32_Phdr);
ptr += sizeof (Elf32_Phdr);
notes->p_offset = offset;
/* NT_PRSTATUS note */
memset (&prstatus, 0, sizeof (prstatus));
if (client->regs)
memcpy (&prstatus.pr_reg, client->regs,
min(client->regs_len, sizeof(elf_gregset_i386_t)));
len = dump_elf32_note (client, ptr, NT_PRSTATUS, "CORE",
(char *)&prstatus, sizeof (prstatus));
offset += len;
ptr += len;
notes->p_filesz += len;
/* NT_PRPSINFO note */
memset(&prpsinfo, 0, sizeof (struct elf_prpsinfo));
prpsinfo.pr_state = 0;
prpsinfo.pr_sname = 'R';
prpsinfo.pr_zomb = 0;
strcpy(prpsinfo.pr_fname, "vmlinux");
len = dump_elf32_note (client, ptr, NT_PRPSINFO, "CORE",
(char *)&prpsinfo, sizeof (prpsinfo));
offset += len;
ptr += len;
notes->p_filesz += len;
/* NT_TASKSTRUCT note */
task_struct = (u32)client->task_struct;
if (BYTE_SWAP_REQUIRED(client))
task_struct = swab32(task_struct);
len = dump_elf32_note (client, ptr, NT_TASKSTRUCT, "CORE",
(char *)&task_struct, sizeof(u32));
offset += len;
ptr += len;
notes->p_filesz += len;
/* Page align offset */
offset = roundup (offset, client->page_size);
load->p_offset = offset;
client->data_offset = offset;
if (BYTE_SWAP_REQUIRED(client)) {
Elf32_Ehdr_swab(client, elf);
Elf32_Phdr_swab(client, notes);
Elf32_Phdr_swab(client, load);
}
while (offset > 0)
{
len = write (client->dump_fd,
buffer + (client->data_offset - offset),
offset);
if (len < 0)
{
g_free (buffer);
return TRUE;
}
offset -= len;
}
g_free (buffer);
return FALSE;
}
static size_t
dump_elf64_note (NetdumpClient *client,
char *buf,
Elf64_Word type,
char *name,
char *desc, int d_len)
{
Elf64_Nhdr *note;
size_t len;
note = (Elf64_Nhdr *)buf;
note->n_namesz = strlen (name);
note->n_descsz = d_len;
note->n_type = type;
len = sizeof (Elf64_Nhdr);
memcpy (buf + len, name, note->n_namesz);
len = roundup (len + note->n_namesz, 4);
memcpy (buf + len, desc, note->n_descsz);
len = roundup (len + note->n_descsz, 4);
if (BYTE_SWAP_REQUIRED(client))
Elf64_Nhdr_swab(client, note);
return len;
}
static gboolean
generate_x86_64_elf_header (NetdumpClient *client)
{
char *buffer, *ptr;
Elf64_Ehdr *elf;
Elf64_Phdr *notes;
Elf64_Phdr *load;
size_t offset, len;
struct elf_prpsinfo_64 prpsinfo;
struct elf_prstatus_x86_64 prstatus;
u64 task_struct;
buffer = g_malloc0 (sizeof (Elf64_Ehdr) + 2*sizeof (Elf64_Phdr) +
4096*2); /* 4096*2 should be enough for the notes + roundup */
offset = 0;
ptr = buffer;
/* Elf header */
elf = (Elf64_Ehdr *)ptr;
memcpy (elf->e_ident, ELFMAG, SELFMAG);
elf->e_ident[EI_CLASS] = ELFCLASS64;
elf->e_ident[EI_DATA] = ELFDATA2LSB;
elf->e_ident[EI_VERSION]= EV_CURRENT;
elf->e_ident[EI_OSABI] = ELFOSABI_SYSV;
elf->e_ident[EI_ABIVERSION] = 0;
memset (elf->e_ident+EI_PAD, 0, EI_NIDENT-EI_PAD);
elf->e_type = ET_CORE;
elf->e_machine = EM_X86_64;
elf->e_version = EV_CURRENT;
elf->e_entry = 0;
elf->e_phoff = sizeof (Elf64_Ehdr);
elf->e_shoff = 0;
elf->e_flags = 0;
elf->e_ehsize = sizeof (Elf64_Ehdr);
elf->e_phentsize = sizeof (Elf64_Phdr);
elf->e_phnum = 2;
elf->e_shentsize = 0;
elf->e_shnum = 0;
elf->e_shstrndx = 0;
offset += sizeof (Elf64_Ehdr);
ptr += sizeof (Elf64_Ehdr);
/* PT_NOTE */
notes = (Elf64_Phdr *)ptr;
notes->p_type = PT_NOTE;
notes->p_offset = 0; /* TO BE FILLED IN */
notes->p_vaddr = 0;
notes->p_paddr = 0;
notes->p_filesz = 0; /* TO BE FILLED IN */
notes->p_memsz = 0;
notes->p_flags = 0;
notes->p_align = 0;
offset += sizeof (Elf64_Phdr);
ptr += sizeof (Elf64_Phdr);
/* PT_LOAD */
load = (Elf64_Phdr *)ptr;
load->p_type = PT_LOAD;
load->p_offset = 0; /* TO BE FILLED IN */
load->p_vaddr = (Elf64_Addr)client->page_offset;
load->p_paddr = 0;
load->p_filesz = client->page_size * client->nr_pages;
load->p_memsz = load->p_filesz;
load->p_flags = PF_R|PF_W|PF_X;
load->p_align = client->page_size;
offset += sizeof (Elf64_Phdr);
ptr += sizeof (Elf64_Phdr);
notes->p_offset = offset;
/* NT_PRSTATUS note */
memset (&prstatus, 0, sizeof (prstatus));
if (client->regs)
memcpy (&prstatus.pr_reg, client->regs,
min(client->regs_len, sizeof(elf_gregset_x86_64_t)));
len = dump_elf64_note (client, ptr, NT_PRSTATUS, "CORE",
(char *)&prstatus, sizeof (prstatus));
offset += len;
ptr += len;
notes->p_filesz += len;
/* NT_PRPSINFO note */
memset(&prpsinfo, 0, sizeof (struct elf_prpsinfo));
prpsinfo.pr_state = 0;
prpsinfo.pr_sname = 'R';
prpsinfo.pr_zomb = 0;
strcpy(prpsinfo.pr_fname, "vmlinux");
len = dump_elf64_note (client, ptr, NT_PRPSINFO, "CORE",
(char *)&prpsinfo, sizeof (prpsinfo));
offset += len;
ptr += len;
notes->p_filesz += len;
/* NT_TASKSTRUCT note */
task_struct = client->task_struct;
if (BYTE_SWAP_REQUIRED(client))
task_struct = swab64(task_struct);
len = dump_elf64_note (client, ptr, NT_TASKSTRUCT, "CORE",
(char *)&task_struct, sizeof(u64));
offset += len;
ptr += len;
notes->p_filesz += len;
/* Page align offset */
offset = roundup (offset, client->page_size);
load->p_offset = offset;
client->data_offset = offset;
if (BYTE_SWAP_REQUIRED(client)) {
Elf64_Ehdr_swab(client, elf);
Elf64_Phdr_swab(client, notes);
Elf64_Phdr_swab(client, load);
}
while (offset > 0)
{
len = write (client->dump_fd,
buffer + (client->data_offset - offset),
offset);
if (len < 0)
{
g_free (buffer);
return TRUE;
}
offset -= len;
}
g_free (buffer);
return FALSE;
}
static gboolean
generate_ppc64_elf_header (NetdumpClient *client)
{
char *buffer, *ptr;
Elf64_Ehdr *elf;
Elf64_Phdr *notes;
Elf64_Phdr *load;
size_t offset, len;
struct elf_prpsinfo_64 prpsinfo;
struct elf_prstatus_ppc64 prstatus;
u64 task_struct;
buffer = g_malloc0 (sizeof (Elf64_Ehdr) + 2*sizeof (Elf64_Phdr) +
4096*2); /* 4096*2 should be enough for the notes + roundup */
offset = 0;
ptr = buffer;
/* Elf header */
elf = (Elf64_Ehdr *)ptr;
memcpy (elf->e_ident, ELFMAG, SELFMAG);
elf->e_ident[EI_CLASS] = ELFCLASS64;
elf->e_ident[EI_DATA] = ELFDATA2MSB;
elf->e_ident[EI_VERSION]= EV_CURRENT;
elf->e_ident[EI_OSABI] = ELFOSABI_SYSV;
elf->e_ident[EI_ABIVERSION] = 0;
memset (elf->e_ident+EI_PAD, 0, EI_NIDENT-EI_PAD);
elf->e_type = ET_CORE;
elf->e_machine = EM_PPC64;
elf->e_version = EV_CURRENT;
elf->e_entry = 0;
elf->e_phoff = sizeof (Elf64_Ehdr);
elf->e_shoff = 0;
elf->e_flags = 0;
elf->e_ehsize = sizeof (Elf64_Ehdr);
elf->e_phentsize = sizeof (Elf64_Phdr);
elf->e_phnum = 2;
elf->e_shentsize = 0;
elf->e_shnum = 0;
elf->e_shstrndx = 0;
offset += sizeof (Elf64_Ehdr);
ptr += sizeof (Elf64_Ehdr);
/* PT_NOTE */
notes = (Elf64_Phdr *)ptr;
notes->p_type = PT_NOTE;
notes->p_offset = 0; /* TO BE FILLED IN */
notes->p_vaddr = 0;
notes->p_paddr = 0;
notes->p_filesz = 0; /* TO BE FILLED IN */
notes->p_memsz = 0;
notes->p_flags = 0;
notes->p_align = 0;
offset += sizeof (Elf64_Phdr);
ptr += sizeof (Elf64_Phdr);
/* PT_LOAD */
load = (Elf64_Phdr *)ptr;
load->p_type = PT_LOAD;
load->p_offset = 0; /* TO BE FILLED IN */
load->p_vaddr = (Elf64_Addr)client->page_offset;
load->p_paddr = 0;
load->p_filesz = client->page_size * client->nr_pages;
load->p_memsz = load->p_filesz;
load->p_flags = PF_R|PF_W|PF_X;
load->p_align = client->page_size;
offset += sizeof (Elf64_Phdr);
ptr += sizeof (Elf64_Phdr);
notes->p_offset = offset;
/* NT_PRSTATUS note */
memset (&prstatus, 0, sizeof (prstatus));
if (client->regs)
memcpy (&prstatus.pr_reg, client->regs,
min(client->regs_len, sizeof(elf_gregset_ppc64_t)));
len = dump_elf64_note (client, ptr, NT_PRSTATUS, "CORE",
(char *)&prstatus, sizeof (prstatus));
offset += len;
ptr += len;
notes->p_filesz += len;
/* NT_PRPSINFO note */
memset(&prpsinfo, 0, sizeof (struct elf_prpsinfo));
prpsinfo.pr_state = 0;
prpsinfo.pr_sname = 'R';
prpsinfo.pr_zomb = 0;
strcpy(prpsinfo.pr_fname, "vmlinux");
len = dump_elf64_note (client, ptr, NT_PRPSINFO, "CORE",
(char *)&prpsinfo, sizeof (prpsinfo));
offset += len;
ptr += len;
notes->p_filesz += len;
/* NT_TASKSTRUCT note */
task_struct = client->task_struct;
if (BYTE_SWAP_REQUIRED(client))
task_struct = swab64(task_struct);
len = dump_elf64_note (client, ptr, NT_TASKSTRUCT, "CORE",
(char *)&task_struct, sizeof(u64));
offset += len;
ptr += len;
notes->p_filesz += len;
/* Page align offset */
offset = roundup (offset, client->page_size);
load->p_offset = offset;
client->data_offset = offset;
if (BYTE_SWAP_REQUIRED(client)) {
Elf64_Ehdr_swab(client, elf);
Elf64_Phdr_swab(client, notes);
Elf64_Phdr_swab(client, load);
}
while (offset > 0)
{
len = write (client->dump_fd,
buffer + (client->data_offset - offset),
offset);
if (len < 0)
{
g_free (buffer);
return TRUE;
}
offset -= len;
}
g_free (buffer);
return FALSE;
}
static gboolean
generate_ia64_elf_header (NetdumpClient *client)
{
char *buffer, *ptr;
Elf64_Ehdr *elf;
Elf64_Phdr *notes;
Elf64_Phdr *load;
size_t offset, len;
struct elf_prpsinfo_64 prpsinfo;
struct elf_prstatus_ia64 prstatus;
u64 task_struct;
buffer = g_malloc0 (sizeof (Elf64_Ehdr) + 2*sizeof (Elf64_Phdr) +
4096*2); /* 4096*2 should be enough for the notes + roundup */
offset = 0;
ptr = buffer;
/* Elf header */
elf = (Elf64_Ehdr *)ptr;
memcpy (elf->e_ident, ELFMAG, SELFMAG);
elf->e_ident[EI_CLASS] = ELFCLASS64;
elf->e_ident[EI_DATA] = ELFDATA2LSB;
elf->e_ident[EI_VERSION]= EV_CURRENT;
elf->e_ident[EI_OSABI] = ELFOSABI_SYSV;
elf->e_ident[EI_ABIVERSION] = 0;
memset (elf->e_ident+EI_PAD, 0, EI_NIDENT-EI_PAD);
elf->e_type = ET_CORE;
elf->e_machine = EM_IA_64;
elf->e_version = EV_CURRENT;
elf->e_entry = 0;
elf->e_phoff = sizeof (Elf64_Ehdr);
elf->e_shoff = 0;
elf->e_flags = 0;
elf->e_ehsize = sizeof (Elf64_Ehdr);
elf->e_phentsize = sizeof (Elf64_Phdr);
elf->e_phnum = 2;
elf->e_shentsize = 0;
elf->e_shnum = 0;
elf->e_shstrndx = 0;
offset += sizeof (Elf64_Ehdr);
ptr += sizeof (Elf64_Ehdr);
/* PT_NOTE */
notes = (Elf64_Phdr *)ptr;
notes->p_type = PT_NOTE;
notes->p_offset = 0; /* TO BE FILLED IN */
notes->p_vaddr = 0;
notes->p_paddr = 0;
notes->p_filesz = 0; /* TO BE FILLED IN */
notes->p_memsz = 0;
notes->p_flags = 0;
notes->p_align = 0;
offset += sizeof (Elf64_Phdr);
ptr += sizeof (Elf64_Phdr);
/* PT_LOAD */
load = (Elf64_Phdr *)ptr;
load->p_type = PT_LOAD;
load->p_offset = 0; /* TO BE FILLED IN */
load->p_vaddr = (Elf64_Addr)client->page_offset;
load->p_paddr = 0;
load->p_filesz = client->page_size * client->nr_pages;
load->p_memsz = load->p_filesz;
load->p_flags = PF_R|PF_W|PF_X;
load->p_align = client->page_size;
offset += sizeof (Elf64_Phdr);
ptr += sizeof (Elf64_Phdr);
notes->p_offset = offset;
/* NT_PRSTATUS note */
memset (&prstatus, 0, sizeof (prstatus));
if (client->regs)
memcpy (&prstatus.pr_reg, client->regs,
min(client->regs_len, sizeof(elf_gregset_ia64_t)));
len = dump_elf64_note (client, ptr, NT_PRSTATUS, "CORE",
(char *)&prstatus, sizeof (prstatus));
offset += len;
ptr += len;
notes->p_filesz += len;
/* NT_PRPSINFO note */
memset(&prpsinfo, 0, sizeof (struct elf_prpsinfo));
prpsinfo.pr_state = 0;
prpsinfo.pr_sname = 'R';
prpsinfo.pr_zomb = 0;
strcpy(prpsinfo.pr_fname, "vmlinux");
len = dump_elf64_note (client, ptr, NT_PRPSINFO, "CORE",
(char *)&prpsinfo, sizeof (prpsinfo));
offset += len;
ptr += len;
notes->p_filesz += len;
/* NT_TASKSTRUCT note */
task_struct = client->task_struct;
if (BYTE_SWAP_REQUIRED(client))
task_struct = swab64(task_struct);
len = dump_elf64_note (client, ptr, NT_TASKSTRUCT, "CORE",
(char *)&task_struct, sizeof(u64));
offset += len;
ptr += len;
notes->p_filesz += len;
/* Page align offset */
offset = roundup (offset, client->page_size);
load->p_offset = offset;
client->data_offset = offset;
if (BYTE_SWAP_REQUIRED(client)) {
Elf64_Ehdr_swab(client, elf);
Elf64_Phdr_swab(client, notes);
Elf64_Phdr_swab(client, load);
}
while (offset > 0)
{
len = write (client->dump_fd,
buffer + (client->data_offset - offset),
offset);
if (len < 0)
{
g_free (buffer);
return TRUE;
}
offset -= len;
}
g_free (buffer);
return FALSE;
}
/********************************************************************************
* Status *
********************************************************************************/
static void
status_request (NetdumpClient *client, gboolean first)
{
guint timeout_delay;
if (first)
client->n_timeouts = 0;
#ifdef DEBUG
g_print ("status_request (%p)\n", client);
#endif
timeout_delay = NETDUMP_INITIAL_TIMEOUT_MSEC*10;
netdump_client_send_request (client,
COMM_SHOW_STATE,
0, 0, TRUE,
&timeout_delay);
client->got_log_packet = FALSE;
client->process_packet = status_packet;
client->timeout = status_timeout;
}
static void
status_packet (NetdumpClient *client,
struct sockaddr_in *addr,
unsigned char *buffer,
int buf_len)
{
reply_t reply;
if (parse_packet (buffer, buf_len, &reply))
{
syslog (LOG_WARNING, "Got invalid packet from %s\n", client->ip_addr);
return;
}
if (reply.code == REPLY_LOG)
{
char *file;
int fd;
file = g_strconcat (client->dump_dir, "/log", NULL);
fd = open (file, O_WRONLY | O_CREAT | O_APPEND, S_IRUSR| S_IWUSR);
g_free (file);
if (fd != -1)
{
write (fd,
buffer + 1 + sizeof (reply_t),
buf_len - (1 + sizeof (reply_t)));
close (fd);
}
client->got_log_packet = TRUE;
/* Extend timeout: */
netdump_client_remove_timeout (client);
netdump_client_setup_timeout (client,
client->outstanding_request,
NETDUMP_INITIAL_TIMEOUT_MSEC*10);
return;
}
/* If out of order reply, ignore */
if (reply.nr != client->outstanding_request)
return;
/* If strange reply, ignore. The timeout will catch this. */
if (reply.code != REPLY_SHOW_STATE)
return;
netdump_client_remove_timeout (client);
/* Now request reboot */
netdump_client_destroy (client, TRUE);
}
static void
status_timeout (NetdumpClient *client,
guint32 request)
{
client->n_timeouts++;
if (client->n_timeouts <= 1 && !client->got_log_packet)
status_request (client, FALSE);
else
{
syslog (LOG_WARNING, "Got too many timeouts waiting for SHOW_STATUS for client %s, rebooting it\n", client->ip_addr);
netdump_client_destroy (client, TRUE);
}
}
syntax highlighted by Code2HTML, v. 0.9.1