#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); } }