mirror of
https://github.com/ioacademy-jikim/debugging
synced 2025-06-09 00:46:12 +00:00
1283 lines
41 KiB
C
1283 lines
41 KiB
C
|
|
/*--------------------------------------------------------------------*/
|
|
/*--- A simple debuginfo server for Valgrind. ---*/
|
|
/*--- valgrind-di-server.c ---*/
|
|
/*--------------------------------------------------------------------*/
|
|
|
|
/* To build for an x86_64-linux host:
|
|
gcc -g -Wall -O -o valgrind-di-server \
|
|
auxprogs/valgrind-di-server.c -Icoregrind -Iinclude \
|
|
-IVEX/pub -DVGO_linux -DVGA_amd64
|
|
|
|
To build for an x86 (32-bit) host
|
|
The same, except change -DVGA_amd64 to -DVGA_x86
|
|
*/
|
|
|
|
/*
|
|
This file is part of Valgrind, a dynamic binary instrumentation
|
|
framework.
|
|
|
|
Copyright (C) 2013-2015 Mozilla Foundation
|
|
|
|
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.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
|
02111-1307, USA.
|
|
|
|
The GNU General Public License is contained in the file COPYING.
|
|
*/
|
|
|
|
/* Contributed by Julian Seward <jseward@acm.org> */
|
|
|
|
/* This code works (just), but it's a mess. Cleanups (also for
|
|
coregrind/m_debuginfo/image.c):
|
|
|
|
* Build this file for the host arch, not the target. But how?
|
|
Even Tromey had difficulty figuring out how to do that.
|
|
|
|
* Change the use of pread w/ fd to FILE*, for the file we're
|
|
serving. Or, at least, put a loop around the pread uses
|
|
so that it works correctly in the case where pread reads more
|
|
than zero but less than we asked for.
|
|
|
|
* CRC3 request/response: pass session-IDs back and forth and
|
|
check them
|
|
|
|
* Check that all error cases result in a FAIL frame being returned.
|
|
|
|
* image.c: don't assert in cases where a FAIL frame is returned;
|
|
instead cause the debuginfo reading to fail gracefully. (Not
|
|
sure how to do this)
|
|
|
|
* Improve diagnostic printing
|
|
|
|
* image.c: do we need to do VG_(write_socket) ? Will it work
|
|
just to use ordinary VG_(write) ?
|
|
|
|
* Both files: document the reason for setting TCP_NODELAY
|
|
|
|
* Add a command line argument saying where the served-from
|
|
directory is -- changes clo_serverpath.
|
|
|
|
* Fix up (common up) massive code duplication between client and
|
|
server.
|
|
|
|
* Tidy up the LZO source files; integrate properly in the build
|
|
system.
|
|
*/
|
|
|
|
/*---------------------------------------------------------------*/
|
|
|
|
/* Include valgrind headers before system headers to avoid problems
|
|
with the system headers #defining things which are used as names
|
|
of structure members in vki headers. */
|
|
|
|
#include "pub_core_basics.h"
|
|
#include "pub_core_libcassert.h" // For VG_BUGS_TO
|
|
#include "pub_core_vki.h" // Avoids warnings from
|
|
// pub_core_libcfile.h
|
|
#include "pub_core_libcfile.h" // For VG_CLO_DEFAULT_LOGPORT
|
|
|
|
/* Needed to get a definition for pread() from unistd.h */
|
|
#ifndef _XOPEN_SOURCE
|
|
#define _XOPEN_SOURCE 600
|
|
#endif
|
|
|
|
#include <stdio.h>
|
|
#include <unistd.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
#include <fcntl.h>
|
|
#include <stdlib.h>
|
|
#include <signal.h>
|
|
#include <sys/poll.h>
|
|
#include <sys/types.h>
|
|
#include <sys/socket.h>
|
|
#include <netinet/in.h>
|
|
#include <sys/stat.h>
|
|
#include <netinet/tcp.h>
|
|
|
|
#include "../coregrind/m_debuginfo/minilzo.h"
|
|
|
|
/*---------------------------------------------------------------*/
|
|
|
|
/* The default allowable number of concurrent connections. */
|
|
#define M_CONNECTIONS_DEFAULT 50
|
|
/* The maximum allowable number of concurrent connections. */
|
|
#define M_CONNECTIONS_MAX 5000
|
|
|
|
/* The maximum allowable number of concurrent connections. */
|
|
unsigned M_CONNECTIONS = 0;
|
|
|
|
static const char* clo_serverpath = ".";
|
|
|
|
|
|
/*---------------------------------------------------------------*/
|
|
|
|
__attribute__ ((noreturn))
|
|
static void panic ( const char* str )
|
|
{
|
|
fprintf(stderr,
|
|
"\nvalgrind-di-server: the "
|
|
"'impossible' happened:\n %s\n", str);
|
|
fprintf(stderr,
|
|
"Please report this bug at: %s\n\n", VG_BUGS_TO);
|
|
exit(1);
|
|
}
|
|
|
|
__attribute__ ((noreturn))
|
|
static void my_assert_fail ( const char* expr, const char* file, int line, const char* fn )
|
|
{
|
|
fprintf(stderr,
|
|
"\nvalgrind-di-server: %s:%d (%s): Assertion '%s' failed.\n",
|
|
file, line, fn, expr );
|
|
fprintf(stderr,
|
|
"Please report this bug at: %s\n\n", VG_BUGS_TO);
|
|
exit(1);
|
|
}
|
|
|
|
#undef assert
|
|
|
|
#define assert(expr) \
|
|
((void) ((expr) ? 0 : \
|
|
(my_assert_fail (VG_STRINGIFY(expr), \
|
|
__FILE__, __LINE__, \
|
|
__PRETTY_FUNCTION__), 0)))
|
|
|
|
|
|
/*---------------------------------------------------------------*/
|
|
|
|
/* Allocate some memory. Return iff successful. */
|
|
static void *my_malloc(size_t amount)
|
|
{
|
|
void *p = malloc(amount ?: 1);
|
|
|
|
if (p == NULL) {
|
|
fprintf(stderr, "Memory allocation failed; cannot continue.\n");
|
|
exit(1);
|
|
}
|
|
return p;
|
|
}
|
|
|
|
/*---------------------------------------------------------------*/
|
|
|
|
/* Holds the state that we need to track, for each connection. */
|
|
typedef
|
|
struct {
|
|
// is this entry in use?
|
|
Bool in_use;
|
|
// socket descriptor to communicate with client. Initialised as
|
|
// soon as this entry is created.
|
|
int conn_sd;
|
|
// fd for the file that we are connected to. Zero if not
|
|
// currently connected to any file.
|
|
int file_fd;
|
|
ULong file_size;
|
|
// Session ID
|
|
ULong session_id;
|
|
// How many bytes and chunks sent?
|
|
ULong stats_n_rdok_frames;
|
|
ULong stats_n_read_unz_bytes; // bytes via READ (uncompressed)
|
|
ULong stats_n_read_z_bytes; // bytes via READ (compressed)
|
|
}
|
|
ConnState;
|
|
|
|
/* The state itself. */
|
|
static int conn_count = 0;
|
|
static ConnState *conn_state;
|
|
|
|
/* Issues unique session ID values. */
|
|
static ULong next_session_id = 1;
|
|
|
|
|
|
/*---------------------------------------------------------------*/
|
|
|
|
// Code that is duplicated with the client :-(
|
|
|
|
/* The following Adler-32 checksum code is taken from zlib-1.2.3, which
|
|
has the following copyright notice. */
|
|
/*
|
|
Copyright notice:
|
|
|
|
(C) 1995-2004 Jean-loup Gailly and Mark Adler
|
|
|
|
This software is provided 'as-is', without any express or implied
|
|
warranty. In no event will the authors be held liable for any damages
|
|
arising from the use of this software.
|
|
|
|
Permission is granted to anyone to use this software for any purpose,
|
|
including commercial applications, and to alter it and redistribute it
|
|
freely, subject to the following restrictions:
|
|
|
|
1. The origin of this software must not be misrepresented; you must not
|
|
claim that you wrote the original software. If you use this software
|
|
in a product, an acknowledgment in the product documentation would be
|
|
appreciated but is not required.
|
|
2. Altered source versions must be plainly marked as such, and must not be
|
|
misrepresented as being the original software.
|
|
3. This notice may not be removed or altered from any source distribution.
|
|
|
|
Jean-loup Gailly Mark Adler
|
|
jloup@gzip.org madler@alumni.caltech.edu
|
|
|
|
If you use the zlib library in a product, we would appreciate *not*
|
|
receiving lengthy legal documents to sign. The sources are provided
|
|
for free but without warranty of any kind. The library has been
|
|
entirely written by Jean-loup Gailly and Mark Adler; it does not
|
|
include third-party code.
|
|
|
|
If you redistribute modified sources, we would appreciate that you include
|
|
in the file ChangeLog history information documenting your changes. Please
|
|
read the FAQ for more information on the distribution of modified source
|
|
versions.
|
|
*/
|
|
|
|
/* Update a running Adler-32 checksum with the bytes buf[0..len-1] and
|
|
return the updated checksum. If buf is NULL, this function returns
|
|
the required initial value for the checksum. An Adler-32 checksum is
|
|
almost as reliable as a CRC32 but can be computed much faster. */
|
|
static
|
|
UInt adler32( UInt adler, const UChar* buf, UInt len )
|
|
{
|
|
# define BASE 65521UL /* largest prime smaller than 65536 */
|
|
# define NMAX 5552
|
|
/* NMAX is the largest n such that
|
|
255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */
|
|
|
|
# define DO1(buf,i) {adler += (buf)[i]; sum2 += adler;}
|
|
# define DO2(buf,i) DO1(buf,i); DO1(buf,i+1);
|
|
# define DO4(buf,i) DO2(buf,i); DO2(buf,i+2);
|
|
# define DO8(buf,i) DO4(buf,i); DO4(buf,i+4);
|
|
# define DO16(buf) DO8(buf,0); DO8(buf,8);
|
|
|
|
/* The zlib sources recommend this definition of MOD if the
|
|
processor cannot do integer division in hardware. */
|
|
# define MOD(a) \
|
|
do { \
|
|
if (a >= (BASE << 16)) a -= (BASE << 16); \
|
|
if (a >= (BASE << 15)) a -= (BASE << 15); \
|
|
if (a >= (BASE << 14)) a -= (BASE << 14); \
|
|
if (a >= (BASE << 13)) a -= (BASE << 13); \
|
|
if (a >= (BASE << 12)) a -= (BASE << 12); \
|
|
if (a >= (BASE << 11)) a -= (BASE << 11); \
|
|
if (a >= (BASE << 10)) a -= (BASE << 10); \
|
|
if (a >= (BASE << 9)) a -= (BASE << 9); \
|
|
if (a >= (BASE << 8)) a -= (BASE << 8); \
|
|
if (a >= (BASE << 7)) a -= (BASE << 7); \
|
|
if (a >= (BASE << 6)) a -= (BASE << 6); \
|
|
if (a >= (BASE << 5)) a -= (BASE << 5); \
|
|
if (a >= (BASE << 4)) a -= (BASE << 4); \
|
|
if (a >= (BASE << 3)) a -= (BASE << 3); \
|
|
if (a >= (BASE << 2)) a -= (BASE << 2); \
|
|
if (a >= (BASE << 1)) a -= (BASE << 1); \
|
|
if (a >= BASE) a -= BASE; \
|
|
} while (0)
|
|
# define MOD4(a) \
|
|
do { \
|
|
if (a >= (BASE << 4)) a -= (BASE << 4); \
|
|
if (a >= (BASE << 3)) a -= (BASE << 3); \
|
|
if (a >= (BASE << 2)) a -= (BASE << 2); \
|
|
if (a >= (BASE << 1)) a -= (BASE << 1); \
|
|
if (a >= BASE) a -= BASE; \
|
|
} while (0)
|
|
|
|
UInt sum2;
|
|
UInt n;
|
|
|
|
/* split Adler-32 into component sums */
|
|
sum2 = (adler >> 16) & 0xffff;
|
|
adler &= 0xffff;
|
|
|
|
/* in case user likes doing a byte at a time, keep it fast */
|
|
if (len == 1) {
|
|
adler += buf[0];
|
|
if (adler >= BASE)
|
|
adler -= BASE;
|
|
sum2 += adler;
|
|
if (sum2 >= BASE)
|
|
sum2 -= BASE;
|
|
return adler | (sum2 << 16);
|
|
}
|
|
|
|
/* initial Adler-32 value (deferred check for len == 1 speed) */
|
|
if (buf == NULL)
|
|
return 1L;
|
|
|
|
/* in case short lengths are provided, keep it somewhat fast */
|
|
if (len < 16) {
|
|
while (len--) {
|
|
adler += *buf++;
|
|
sum2 += adler;
|
|
}
|
|
if (adler >= BASE)
|
|
adler -= BASE;
|
|
MOD4(sum2); /* only added so many BASE's */
|
|
return adler | (sum2 << 16);
|
|
}
|
|
|
|
/* do length NMAX blocks -- requires just one modulo operation */
|
|
while (len >= NMAX) {
|
|
len -= NMAX;
|
|
n = NMAX / 16; /* NMAX is divisible by 16 */
|
|
do {
|
|
DO16(buf); /* 16 sums unrolled */
|
|
buf += 16;
|
|
} while (--n);
|
|
MOD(adler);
|
|
MOD(sum2);
|
|
}
|
|
|
|
/* do remaining bytes (less than NMAX, still just one modulo) */
|
|
if (len) { /* avoid modulos if none remaining */
|
|
while (len >= 16) {
|
|
len -= 16;
|
|
DO16(buf);
|
|
buf += 16;
|
|
}
|
|
while (len--) {
|
|
adler += *buf++;
|
|
sum2 += adler;
|
|
}
|
|
MOD(adler);
|
|
MOD(sum2);
|
|
}
|
|
|
|
/* return recombined sums */
|
|
return adler | (sum2 << 16);
|
|
|
|
# undef MOD4
|
|
# undef MOD
|
|
# undef DO16
|
|
# undef DO8
|
|
# undef DO4
|
|
# undef DO2
|
|
# undef DO1
|
|
# undef NMAX
|
|
# undef BASE
|
|
}
|
|
|
|
|
|
/* A frame. The first 4 bytes of |data| give the kind of the frame,
|
|
and the rest of it is kind-specific data. */
|
|
typedef struct { UChar* data; SizeT n_data; } Frame;
|
|
|
|
|
|
static void write_UInt_le ( /*OUT*/UChar* dst, UInt n )
|
|
{
|
|
Int i;
|
|
for (i = 0; i <= 3; i++) {
|
|
dst[i] = (UChar)(n & 0xFF);
|
|
n >>= 8;
|
|
}
|
|
}
|
|
|
|
static UInt read_UInt_le ( UChar* src )
|
|
{
|
|
UInt r = 0;
|
|
Int i;
|
|
for (i = 3; i >= 0; i--) {
|
|
r <<= 8;
|
|
r += (UInt)src[i];
|
|
}
|
|
return r;
|
|
}
|
|
|
|
static void write_ULong_le ( /*OUT*/UChar* dst, ULong n )
|
|
{
|
|
Int i;
|
|
for (i = 0; i <= 7; i++) {
|
|
dst[i] = (UChar)(n & 0xFF);
|
|
n >>= 8;
|
|
}
|
|
}
|
|
|
|
static ULong read_ULong_le ( UChar* src )
|
|
{
|
|
ULong r = 0;
|
|
Int i;
|
|
for (i = 7; i >= 0; i--) {
|
|
r <<= 8;
|
|
r += (ULong)src[i];
|
|
}
|
|
return r;
|
|
}
|
|
|
|
static Frame* mk_Frame_asciiz ( const char* tag, const char* str )
|
|
{
|
|
assert(strlen(tag) == 4);
|
|
Frame* f = calloc(sizeof(Frame), 1);
|
|
size_t n_str = strlen(str);
|
|
f->n_data = 4 + n_str + 1;
|
|
f->data = calloc(f->n_data, 1);
|
|
memcpy(&f->data[0], tag, 4);
|
|
memcpy(&f->data[4], str, n_str);
|
|
assert(f->data[4 + n_str] == 0);
|
|
return f;
|
|
}
|
|
|
|
static Bool parse_Frame_noargs ( Frame* fr, const HChar* tag )
|
|
{
|
|
assert(strlen(tag) == 4);
|
|
if (!fr || !fr->data) return False;
|
|
if (fr->n_data < 4) return False;
|
|
if (memcmp(&fr->data[0], tag, 4) != 0) return False;
|
|
if (fr->n_data != 4) return False;
|
|
return True;
|
|
}
|
|
|
|
static Bool parse_Frame_asciiz ( Frame* fr, const HChar* tag,
|
|
/*OUT*/UChar** str )
|
|
{
|
|
assert(strlen(tag) == 4);
|
|
if (!fr || !fr->data) return False;
|
|
if (fr->n_data < 4) return False;
|
|
if (memcmp(&fr->data[0], tag, 4) != 0) return False;
|
|
if (fr->n_data < 5) return False; // else there isn't even enough
|
|
// space for the terminating zero
|
|
/* Find the terminating zero and ensure it's right at the end
|
|
of the data. If not, the frame is malformed. */
|
|
SizeT i = 4;
|
|
while (True) {
|
|
if (i >= fr->n_data) break;
|
|
if (fr->data[i] == 0) break;
|
|
i++;
|
|
}
|
|
assert(i <= fr->n_data);
|
|
if (i == fr->n_data-1 && fr->data[i] == 0) {
|
|
*str = &fr->data[4];
|
|
return True;
|
|
} else {
|
|
return False;
|
|
}
|
|
}
|
|
|
|
static Frame* mk_Frame_le64 ( const HChar* tag, ULong n1 )
|
|
{
|
|
assert(strlen(tag) == 4);
|
|
Frame* f = calloc(sizeof(Frame), 1);
|
|
f->n_data = 4 + 1*8;
|
|
f->data = calloc(f->n_data, 1);
|
|
memcpy(&f->data[0], tag, 4);
|
|
write_ULong_le(&f->data[4 + 0*8], n1);
|
|
return f;
|
|
}
|
|
|
|
static Frame* mk_Frame_le64_le64 ( const HChar* tag, ULong n1, ULong n2 )
|
|
{
|
|
assert(strlen(tag) == 4);
|
|
Frame* f = calloc(sizeof(Frame), 1);
|
|
f->n_data = 4 + 2*8;
|
|
f->data = calloc(f->n_data, 1);
|
|
memcpy(&f->data[0], tag, 4);
|
|
write_ULong_le(&f->data[4 + 0*8], n1);
|
|
write_ULong_le(&f->data[4 + 1*8], n2);
|
|
return f;
|
|
}
|
|
|
|
static Bool parse_Frame_le64_le64_le64 ( Frame* fr, const HChar* tag,
|
|
/*OUT*/ULong* n1, /*OUT*/ULong* n2,
|
|
/*OUT*/ULong* n3 )
|
|
{
|
|
assert(strlen(tag) == 4);
|
|
if (!fr || !fr->data) return False;
|
|
if (fr->n_data < 4) return False;
|
|
if (memcmp(&fr->data[0], tag, 4) != 0) return False;
|
|
if (fr->n_data != 4 + 3*8) return False;
|
|
*n1 = read_ULong_le(&fr->data[4 + 0*8]);
|
|
*n2 = read_ULong_le(&fr->data[4 + 1*8]);
|
|
*n3 = read_ULong_le(&fr->data[4 + 2*8]);
|
|
return True;
|
|
}
|
|
|
|
static Frame* mk_Frame_le64_le64_le64_bytes (
|
|
const HChar* tag,
|
|
ULong n1, ULong n2, ULong n3, ULong n_data,
|
|
/*OUT*/UChar** data )
|
|
{
|
|
assert(strlen(tag) == 4);
|
|
Frame* f = calloc(sizeof(Frame), 1);
|
|
f->n_data = 4 + 3*8 + n_data;
|
|
f->data = calloc(f->n_data, 1);
|
|
memcpy(&f->data[0], tag, 4);
|
|
write_ULong_le(&f->data[4 + 0*8], n1);
|
|
write_ULong_le(&f->data[4 + 1*8], n2);
|
|
write_ULong_le(&f->data[4 + 2*8], n3);
|
|
*data = &f->data[4 + 3*8];
|
|
return f;
|
|
}
|
|
|
|
static void free_Frame ( Frame* fr )
|
|
{
|
|
assert(fr && fr->data);
|
|
free(fr->data);
|
|
free(fr);
|
|
}
|
|
|
|
|
|
static void set_blocking ( int sd )
|
|
{
|
|
int res;
|
|
res = fcntl(sd, F_GETFL);
|
|
res = fcntl(sd, F_SETFL, res & ~O_NONBLOCK);
|
|
if (res != 0) {
|
|
perror("fcntl failed");
|
|
panic("set_blocking");
|
|
}
|
|
}
|
|
|
|
|
|
#if 0
|
|
static void set_nonblocking ( int sd )
|
|
{
|
|
int res;
|
|
res = fcntl(sd, F_GETFL);
|
|
res = fcntl(sd, F_SETFL, res | O_NONBLOCK);
|
|
if (res != 0) {
|
|
perror("fcntl failed");
|
|
panic("set_nonblocking");
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
/* Tries to read 'len' bytes from fd, blocking if necessary. Assumes
|
|
fd has been set in blocking mode. If it returns with the number of
|
|
bytes read < len, it means that either fd was closed, or there was
|
|
an error on it. */
|
|
static SizeT my_read ( Int fd, UChar* buf, SizeT len )
|
|
{
|
|
//set_blocking(fd);
|
|
SizeT nRead = 0;
|
|
while (1) {
|
|
if (nRead == len) return nRead;
|
|
assert(nRead < len);
|
|
SizeT nNeeded = len - nRead;
|
|
assert(nNeeded > 0);
|
|
SSizeT n = read(fd, &buf[nRead], nNeeded);
|
|
if (n <= 0) return nRead; /* error or EOF */
|
|
nRead += n;
|
|
}
|
|
}
|
|
|
|
/* Tries to write 'len' bytes to fd, blocking if necessary. Assumes
|
|
fd has been set in blocking mode. If it returns with the number of
|
|
bytes written < len, it means that either fd was closed, or there was
|
|
an error on it. */
|
|
static SizeT my_write ( Int fd, UChar* buf, SizeT len )
|
|
{
|
|
//set_nonblocking(fd);
|
|
SizeT nWritten = 0;
|
|
while (1) {
|
|
if (nWritten == len) return nWritten;
|
|
assert(nWritten < len);
|
|
SizeT nStillToDo = len - nWritten;
|
|
assert(nStillToDo > 0);
|
|
SSizeT n = write(fd, &buf[nWritten], nStillToDo);
|
|
if (n < 0) return nWritten; /* error or EOF */
|
|
nWritten += n;
|
|
}
|
|
}
|
|
|
|
|
|
static UInt calc_gnu_debuglink_crc32(/*OUT*/Bool* ok, int fd, ULong size)
|
|
{
|
|
static const UInt crc32_table[256] =
|
|
{
|
|
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419,
|
|
0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4,
|
|
0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07,
|
|
0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
|
|
0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856,
|
|
0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
|
|
0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4,
|
|
0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
|
|
0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3,
|
|
0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a,
|
|
0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599,
|
|
0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
|
|
0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190,
|
|
0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f,
|
|
0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e,
|
|
0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
|
|
0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed,
|
|
0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
|
|
0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3,
|
|
0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
|
|
0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a,
|
|
0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5,
|
|
0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010,
|
|
0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
|
|
0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17,
|
|
0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6,
|
|
0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615,
|
|
0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
|
|
0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344,
|
|
0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
|
|
0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a,
|
|
0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
|
|
0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1,
|
|
0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c,
|
|
0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef,
|
|
0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
|
|
0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe,
|
|
0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31,
|
|
0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c,
|
|
0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
|
|
0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b,
|
|
0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
|
|
0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1,
|
|
0x18b74777, 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
|
|
0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278,
|
|
0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7,
|
|
0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66,
|
|
0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
|
|
0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605,
|
|
0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8,
|
|
0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b,
|
|
0x2d02ef8d
|
|
};
|
|
|
|
/* Work through the image in 1 KB chunks. */
|
|
UInt crc = 0xFFFFFFFF;
|
|
ULong img_szB = size;
|
|
ULong curr_off = 0;
|
|
while (1) {
|
|
assert(curr_off >= 0 && curr_off <= img_szB);
|
|
if (curr_off == img_szB) break;
|
|
ULong avail = img_szB - curr_off;
|
|
assert(avail > 0 && avail <= img_szB);
|
|
if (avail > 65536) avail = 65536;
|
|
UChar buf[65536];
|
|
Int nRead = pread(fd, buf, avail, curr_off);
|
|
if (nRead <= 0) { /* EOF or error on the file; neither should happen */
|
|
*ok = False;
|
|
return 0;
|
|
}
|
|
/* this is a kludge .. we should loop around pread and deal
|
|
with short reads, for whatever reason */
|
|
assert(nRead == avail);
|
|
UInt i;
|
|
for (i = 0; i < (UInt)nRead; i++)
|
|
crc = crc32_table[(crc ^ buf[i]) & 0xff] ^ (crc >> 8);
|
|
curr_off += nRead;
|
|
}
|
|
*ok = True;
|
|
return ~crc & 0xFFFFFFFF;
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------*/
|
|
|
|
/* Handle a transaction for conn_state[conn_no]. There is incoming
|
|
data available; read it and send back an appropriate response.
|
|
Returns a boolean indicating whether the connection has been
|
|
closed; in which case this function does all necessary cleanup and
|
|
leaves conn_state[conn_no] in a not-in-use state. */
|
|
|
|
static Bool handle_transaction ( int conn_no )
|
|
{
|
|
Frame* req = NULL; /* the request frame that we receive */
|
|
Frame* res = NULL; /* the response frame that we send back */
|
|
|
|
assert(conn_no >= 0 && conn_no < M_CONNECTIONS);
|
|
assert(conn_state[conn_no].in_use);
|
|
|
|
//printf("SERVER: handle_transaction(%d)\n", conn_no); fflush(stdout);
|
|
|
|
Int sd = conn_state[conn_no].conn_sd;
|
|
|
|
/* Get a frame out of the channel. */
|
|
UChar rd_first8[8]; // adler32; length32
|
|
{ Int r = my_read(sd, &rd_first8[0], 8);
|
|
if (r == 0) goto client_closed_conn;
|
|
if (r != 8) goto fail;
|
|
}
|
|
UInt rd_adler = read_UInt_le(&rd_first8[0]);
|
|
UInt rd_len = read_UInt_le(&rd_first8[4]);
|
|
/* Allocate a Frame to hold the result data, and read into it. */
|
|
// Reject obviously-insane length fields.
|
|
if (rd_len > 4*1024*1024) goto fail;
|
|
assert(req == NULL);
|
|
req = calloc(sizeof(Frame), 1);
|
|
req->n_data = rd_len;
|
|
req->data = calloc(rd_len, 1);
|
|
if (rd_len > 0) {
|
|
Int r = my_read(sd, req->data, req->n_data);
|
|
if (r != rd_len) goto fail;
|
|
}
|
|
//printf("SERVER: recv %c%c%c%c\n", req->data[0], req->data[1], req->data[2], req->data[3]); fflush(stdout);
|
|
|
|
/* Compute the checksum for the received data, and check it. */
|
|
UInt adler = adler32(0, NULL, 0); // initial value
|
|
adler = adler32(adler, &rd_first8[4], 4);
|
|
if (req->n_data > 0)
|
|
adler = adler32(adler, req->data, req->n_data);
|
|
|
|
if (adler/*computed*/ != rd_adler/*expected*/) goto fail;
|
|
|
|
/* Now we have a request frame. Cook up a response. */
|
|
assert(res == NULL);
|
|
|
|
UChar* filename = NULL;
|
|
ULong req_session_id = 0, req_offset = 0, req_len = 0;
|
|
|
|
if (parse_Frame_noargs(req, "VERS")) {
|
|
res = mk_Frame_asciiz("VEOK", "Valgrind Debuginfo Server, Version 1");
|
|
}
|
|
else
|
|
if (parse_Frame_noargs(req, "CRC3")) {
|
|
/* FIXME: add a session ID to this request, and check it */
|
|
if (conn_state[conn_no].file_fd == 0) {
|
|
res = mk_Frame_asciiz("CRC3", "FAIL: not connected to file");
|
|
} else {
|
|
Bool ok = False;
|
|
UInt crc32 = calc_gnu_debuglink_crc32(&ok,
|
|
conn_state[conn_no].file_fd,
|
|
conn_state[conn_no].file_size);
|
|
if (ok) {
|
|
res = mk_Frame_le64("CROK", (ULong)crc32);
|
|
} else {
|
|
res = mk_Frame_asciiz("FAIL", "CRC3: I/O error reading file");
|
|
}
|
|
}
|
|
}
|
|
else
|
|
if (parse_Frame_asciiz(req, "OPEN", &filename)) {
|
|
Bool ok = True;
|
|
int fd = 0;
|
|
if (conn_state[conn_no].file_fd != 0) {
|
|
res = mk_Frame_asciiz("FAIL", "OPEN: already connected to file");
|
|
ok = False;
|
|
}
|
|
if (ok) {
|
|
assert(clo_serverpath);
|
|
fd = open((char*)filename, O_RDONLY);
|
|
if (fd == -1) {
|
|
res = mk_Frame_asciiz("FAIL", "OPEN: cannot open file");
|
|
printf("(%d) SessionID %llu: open failed for \"%s\"\n",
|
|
conn_count, conn_state[conn_no].session_id, filename );
|
|
ok = False;
|
|
} else {
|
|
assert(fd > 2);
|
|
}
|
|
}
|
|
if (ok) {
|
|
struct stat stat_buf;
|
|
int r = fstat(fd, &stat_buf);
|
|
if (r != 0) {
|
|
res = mk_Frame_asciiz("FAIL", "OPEN: cannot stat file");
|
|
ok = False;
|
|
}
|
|
if (ok && stat_buf.st_size == 0) {
|
|
res = mk_Frame_asciiz("FAIL", "OPEN: file has zero size");
|
|
ok = False;
|
|
}
|
|
if (ok) {
|
|
conn_state[conn_no].file_fd = fd;
|
|
conn_state[conn_no].file_size = stat_buf.st_size;
|
|
assert(res == NULL);
|
|
res = mk_Frame_le64_le64("OPOK", conn_state[conn_no].session_id,
|
|
conn_state[conn_no].file_size);
|
|
printf("(%d) SessionID %llu: open successful for \"%s\"\n",
|
|
conn_count, conn_state[conn_no].session_id, filename );
|
|
fflush(stdout);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
if (parse_Frame_le64_le64_le64(req, "READ", &req_session_id,
|
|
&req_offset, &req_len)) {
|
|
/* Because each new connection is associated with its own socket
|
|
descriptor and hence with a particular conn_no, the requested
|
|
session-ID is redundant -- it must be the one associated with
|
|
this slot. But check anyway. */
|
|
Bool ok = True;
|
|
if (req_session_id != conn_state[conn_no].session_id) {
|
|
res = mk_Frame_asciiz("FAIL", "READ: invalid session ID");
|
|
ok = False;
|
|
}
|
|
/* Check we're connected to a file, and if so range-check the
|
|
request. */
|
|
if (ok && conn_state[conn_no].file_fd == 0) {
|
|
res = mk_Frame_asciiz("FAIL", "READ: no associated file");
|
|
ok = False;
|
|
}
|
|
if (ok && (req_len == 0 || req_len > 4*1024*1024)) {
|
|
res = mk_Frame_asciiz("FAIL", "READ: invalid request size");
|
|
ok = False;
|
|
}
|
|
if (ok && req_len + req_offset > conn_state[conn_no].file_size) {
|
|
res = mk_Frame_asciiz("FAIL", "READ: request exceeds file size");
|
|
ok = False;
|
|
}
|
|
/* Try to read the file. */
|
|
if (ok) {
|
|
/* First, allocate a temp buf and read from the file into it. */
|
|
/* FIXME: what if pread reads short and we have to redo it? */
|
|
UChar* unzBuf = my_malloc(req_len);
|
|
size_t nRead = pread(conn_state[conn_no].file_fd,
|
|
unzBuf, req_len, req_offset);
|
|
if (nRead != req_len) {
|
|
free_Frame(res);
|
|
res = mk_Frame_asciiz("FAIL", "READ: I/O error reading file");
|
|
ok = False;
|
|
}
|
|
if (ok) {
|
|
// Now compress it with LZO. LZO appears to recommend
|
|
// the worst-case output size as (in_len + in_len / 16 + 67).
|
|
// Be more conservative here.
|
|
# define STACK_ALLOC(var,size) \
|
|
lzo_align_t __LZO_MMODEL \
|
|
var [ ((size) \
|
|
+ (sizeof(lzo_align_t) - 1)) / sizeof(lzo_align_t) ]
|
|
STACK_ALLOC(wrkmem, LZO1X_1_MEM_COMPRESS);
|
|
# undef STACK_ALLOC
|
|
UInt zLenMax = req_len + req_len / 4 + 1024;
|
|
UChar* zBuf = my_malloc(zLenMax);
|
|
lzo_uint zLen = zLenMax;
|
|
Int lzo_rc = lzo1x_1_compress(unzBuf, req_len,
|
|
zBuf, &zLen, wrkmem);
|
|
if (lzo_rc == LZO_E_OK) {
|
|
//printf("XXXXX req_len %u zLen %u\n", (UInt)req_len, (UInt)zLen);
|
|
assert(zLen <= zLenMax);
|
|
/* Make a frame to put the results in. Bytes 24 and
|
|
onwards need to be filled from the compressed data,
|
|
and 'buf' is set to point to the right bit. */
|
|
UChar* buf = NULL;
|
|
res = mk_Frame_le64_le64_le64_bytes
|
|
("RDOK", req_session_id, req_offset, req_len, zLen, &buf);
|
|
assert(res);
|
|
assert(buf);
|
|
memcpy(buf, zBuf, zLen);
|
|
// Update stats
|
|
conn_state[conn_no].stats_n_rdok_frames++;
|
|
conn_state[conn_no].stats_n_read_unz_bytes += req_len;
|
|
conn_state[conn_no].stats_n_read_z_bytes += zLen;
|
|
} else {
|
|
ok = False;
|
|
free_Frame(res);
|
|
res = mk_Frame_asciiz("FAIL", "READ: LZO failed");
|
|
}
|
|
free(zBuf);
|
|
}
|
|
free(unzBuf);
|
|
}
|
|
}
|
|
else {
|
|
res = mk_Frame_asciiz("FAIL", "Invalid request frame type");
|
|
}
|
|
|
|
/* All paths through the above should result in an assignment to |res|. */
|
|
assert(res != NULL);
|
|
|
|
/* And send the response frame back to the client. */
|
|
/* What goes on the wire is:
|
|
adler(le32) n_data(le32) data[0 .. n_data-1]
|
|
where the checksum covers n_data as well as data[].
|
|
*/
|
|
/* The initial Adler-32 value */
|
|
adler = adler32(0, NULL, 0);
|
|
|
|
/* Fold in the length field, encoded as le32. */
|
|
UChar wr_first8[8];
|
|
write_UInt_le(&wr_first8[4], res->n_data);
|
|
adler = adler32(adler, &wr_first8[4], 4);
|
|
/* Fold in the data values */
|
|
adler = adler32(adler, res->data, res->n_data);
|
|
write_UInt_le(&wr_first8[0], adler);
|
|
|
|
Int r = my_write(sd, &wr_first8[0], 8);
|
|
if (r != 8) goto fail;
|
|
assert(res->n_data >= 4); // else ill formed -- no KIND field
|
|
r = my_write(sd, res->data, res->n_data);
|
|
if (r != res->n_data) goto fail;
|
|
|
|
//printf("SERVER: send %c%c%c%c\n", res->data[0], res->data[1], res->data[2], res->data[3]); fflush(stdout);
|
|
|
|
/* So, success. */
|
|
free_Frame(req);
|
|
free_Frame(res);
|
|
return False; /* "connection still in use" */
|
|
|
|
// Is there any difference between these?
|
|
client_closed_conn:
|
|
fail:
|
|
if (conn_state[conn_no].conn_sd > 0)
|
|
close(conn_state[conn_no].conn_sd);
|
|
if (conn_state[conn_no].file_fd > 0)
|
|
close(conn_state[conn_no].file_fd);
|
|
|
|
if (conn_state[conn_no].stats_n_rdok_frames > 0) {
|
|
printf("(%d) SessionID %llu: sent %llu frames, "
|
|
"%llu MB (unz), %llu MB (z), ratio %4.2f:1\n",
|
|
conn_count, conn_state[conn_no].session_id,
|
|
conn_state[conn_no].stats_n_rdok_frames,
|
|
conn_state[conn_no].stats_n_read_unz_bytes / 1000000,
|
|
conn_state[conn_no].stats_n_read_z_bytes / 1000000,
|
|
(double)conn_state[conn_no].stats_n_read_unz_bytes
|
|
/ (double)conn_state[conn_no].stats_n_read_z_bytes);
|
|
printf("(%d) SessionID %llu: closed\n",
|
|
conn_count, conn_state[conn_no].session_id);
|
|
|
|
fflush(stdout);
|
|
}
|
|
|
|
memset(&conn_state[conn_no], 0, sizeof(conn_state[conn_no]));
|
|
if (req) free_Frame(req);
|
|
if (res) free_Frame(res);
|
|
return True; /* "connection has been closed" */
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------*/
|
|
|
|
|
|
|
|
#if 0
|
|
static void copyout ( char* buf, int nbuf )
|
|
{
|
|
int i;
|
|
for (i = 0; i < nbuf; i++) {
|
|
if (buf[i] == '\n') {
|
|
fprintf(stdout, "\n(%d) ", conn_count);
|
|
} else {
|
|
__attribute__((unused)) size_t ignored
|
|
= fwrite(&buf[i], 1, 1, stdout);
|
|
}
|
|
}
|
|
fflush(stdout);
|
|
}
|
|
|
|
static int read_from_sd ( int sd )
|
|
{
|
|
char buf[100];
|
|
int n;
|
|
|
|
set_blocking(sd);
|
|
n = read(sd, buf, 99);
|
|
if (n <= 0) return 0; /* closed */
|
|
copyout(buf, n);
|
|
|
|
set_nonblocking(sd);
|
|
while (1) {
|
|
n = read(sd, buf, 100);
|
|
if (n <= 0) return 1; /* not closed */
|
|
copyout(buf, n);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
static void snooze ( void )
|
|
{
|
|
struct timespec req;
|
|
req.tv_sec = 0;
|
|
req.tv_nsec = 200 * 1000 * 1000;
|
|
nanosleep(&req,NULL);
|
|
}
|
|
|
|
|
|
/* returns 0 if negative, or > BOUND or invalid characters were found */
|
|
static int atoi_with_bound ( const char* str, int bound )
|
|
{
|
|
int n = 0;
|
|
while (1) {
|
|
if (*str == 0)
|
|
break;
|
|
if (*str < '0' || *str > '9')
|
|
return 0;
|
|
n = 10*n + (int)(*str - '0');
|
|
str++;
|
|
if (n >= bound)
|
|
return 0;
|
|
}
|
|
return n;
|
|
}
|
|
|
|
|
|
/* returns 0 if invalid, else port # */
|
|
static int atoi_portno ( const char* str )
|
|
{
|
|
int n = atoi_with_bound(str, 65536);
|
|
|
|
if (n < 1024)
|
|
return 0;
|
|
return n;
|
|
}
|
|
|
|
|
|
static void usage ( void )
|
|
{
|
|
fprintf(stderr,
|
|
"\n"
|
|
"usage is:\n"
|
|
"\n"
|
|
" valgrind-di-server [--exit-at-zero|-e] [port-number]\n"
|
|
"\n"
|
|
" where --exit-at-zero or -e causes the listener to exit\n"
|
|
" when the number of connections falls back to zero\n"
|
|
" (the default is to keep listening forever)\n"
|
|
"\n"
|
|
" --max-connect=INT can be used to increase the maximum\n"
|
|
" number of connected processes (default = %d).\n"
|
|
" INT must be positive and less than %d.\n"
|
|
"\n"
|
|
" port-number is the default port on which to listen for\n"
|
|
" connections. It must be between 1024 and 65535.\n"
|
|
" Current default is %d.\n"
|
|
"\n"
|
|
,
|
|
M_CONNECTIONS_DEFAULT, M_CONNECTIONS_MAX, VG_CLO_DEFAULT_LOGPORT
|
|
);
|
|
exit(1);
|
|
}
|
|
|
|
|
|
static void banner ( const char* str )
|
|
{
|
|
time_t t;
|
|
t = time(NULL);
|
|
printf("valgrind-di-server %s at %s", str, ctime(&t));
|
|
fflush(stdout);
|
|
}
|
|
|
|
|
|
static void exit_routine ( void )
|
|
{
|
|
banner("exited");
|
|
exit(0);
|
|
}
|
|
|
|
|
|
static void sigint_handler ( int signo )
|
|
{
|
|
exit_routine();
|
|
}
|
|
|
|
|
|
int main (int argc, char** argv)
|
|
{
|
|
int i, j, res, one;
|
|
int main_sd, new_sd;
|
|
socklen_t client_len;
|
|
struct sockaddr_in client_addr, server_addr;
|
|
|
|
char /*bool*/ exit_when_zero = 0;
|
|
int port = VG_CLO_DEFAULT_LOGPORT;
|
|
|
|
for (i = 1; i < argc; i++) {
|
|
if (0==strcmp(argv[i], "--exit-at-zero")
|
|
|| 0==strcmp(argv[i], "-e")) {
|
|
exit_when_zero = 1;
|
|
}
|
|
else if (0 == strncmp(argv[i], "--max-connect=", 14)) {
|
|
M_CONNECTIONS = atoi_with_bound(strchr(argv[i], '=') + 1, 5000);
|
|
if (M_CONNECTIONS <= 0 || M_CONNECTIONS > M_CONNECTIONS_MAX)
|
|
usage();
|
|
}
|
|
else
|
|
if (atoi_portno(argv[i]) > 0) {
|
|
port = atoi_portno(argv[i]);
|
|
}
|
|
else
|
|
usage();
|
|
}
|
|
|
|
if (M_CONNECTIONS == 0) // nothing specified on command line
|
|
M_CONNECTIONS = M_CONNECTIONS_DEFAULT;
|
|
|
|
conn_state = my_malloc(M_CONNECTIONS * sizeof conn_state[0]);
|
|
|
|
banner("started");
|
|
signal(SIGINT, sigint_handler);
|
|
|
|
conn_count = 0;
|
|
memset(conn_state, 0, M_CONNECTIONS * sizeof conn_state[0]);
|
|
|
|
/* create socket */
|
|
main_sd = socket(AF_INET, SOCK_STREAM, 0);
|
|
if (main_sd < 0) {
|
|
perror("cannot open socket ");
|
|
panic("main -- create socket");
|
|
}
|
|
|
|
/* allow address reuse to avoid "address already in use" errors */
|
|
|
|
one = 1;
|
|
if (setsockopt(main_sd, SOL_SOCKET, SO_REUSEADDR,
|
|
&one, sizeof(one)) < 0) {
|
|
perror("cannot enable address reuse ");
|
|
panic("main -- enable address reuse");
|
|
}
|
|
|
|
/* bind server port */
|
|
server_addr.sin_family = AF_INET;
|
|
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
|
|
server_addr.sin_port = htons(port);
|
|
|
|
if (bind(main_sd, (struct sockaddr *) &server_addr,
|
|
sizeof(server_addr) ) < 0) {
|
|
perror("cannot bind port ");
|
|
panic("main -- bind port");
|
|
}
|
|
|
|
res = listen(main_sd, M_CONNECTIONS);
|
|
if (res != 0) {
|
|
perror("listen failed ");
|
|
panic("main -- listen");
|
|
}
|
|
|
|
Bool do_snooze = False;
|
|
while (1) {
|
|
|
|
if (0 && do_snooze)
|
|
snooze();
|
|
|
|
/* Snooze after this iteration, unless something happened. */
|
|
do_snooze = True;
|
|
|
|
/* enquire, using poll, whether there is any activity available on
|
|
the main socket descriptor. If so, someone is trying to
|
|
connect; get the fd and add it to our table thereof. */
|
|
{ struct pollfd ufd;
|
|
while (1) {
|
|
ufd.fd = main_sd;
|
|
ufd.events = POLLIN;
|
|
ufd.revents = 0;
|
|
res = poll(&ufd, 1, 0/*ms*/ /* 0=return immediately. */);
|
|
if (res == 0) break;
|
|
|
|
/* ok, we have someone waiting to connect. Get the sd. */
|
|
client_len = sizeof(client_addr);
|
|
new_sd = accept(main_sd, (struct sockaddr *)&client_addr,
|
|
&client_len);
|
|
if (new_sd < 0) {
|
|
perror("cannot accept connection ");
|
|
panic("main -- accept connection");
|
|
}
|
|
|
|
/* find a place to put it. */
|
|
assert(new_sd > 0);
|
|
for (i = 0; i < M_CONNECTIONS; i++)
|
|
if (!conn_state[i].in_use)
|
|
break;
|
|
|
|
if (i >= M_CONNECTIONS) {
|
|
fprintf(stderr, "\n\nMore than %d concurrent connections.\n"
|
|
"Restart the server giving --max-connect=INT on the\n"
|
|
"commandline to increase the limit.\n\n",
|
|
M_CONNECTIONS);
|
|
exit(1);
|
|
}
|
|
|
|
assert(one == 1);
|
|
int ret = setsockopt( new_sd, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one));
|
|
assert(ret != -1);
|
|
|
|
memset(&conn_state[i], 0, sizeof(conn_state[i]));
|
|
conn_state[i].in_use = True;
|
|
conn_state[i].conn_sd = new_sd;
|
|
conn_state[i].file_fd = 0; /* not known yet */
|
|
conn_state[i].session_id = next_session_id++;
|
|
set_blocking(new_sd);
|
|
conn_count++;
|
|
do_snooze = False;
|
|
} /* while (1) */
|
|
}
|
|
|
|
/* We've processed all new connect requests. Listen for changes
|
|
to the current set of fds. This requires gathering up all
|
|
the known conn_sd values and doing poll() on them. */
|
|
static struct pollfd *tmp_pollfd;
|
|
if (tmp_pollfd == NULL)
|
|
tmp_pollfd = my_malloc(M_CONNECTIONS * sizeof tmp_pollfd[0]);
|
|
|
|
/* And a parallel array which maps entries in tmp_pollfd back to
|
|
entries in conn_state. */
|
|
static int *tmp_pollfd_to_conn_state;
|
|
if (tmp_pollfd_to_conn_state == NULL)
|
|
tmp_pollfd_to_conn_state =
|
|
my_malloc(M_CONNECTIONS * sizeof tmp_pollfd_to_conn_state[0]);
|
|
|
|
j = 0;
|
|
for (i = 0; i < M_CONNECTIONS; i++) {
|
|
if (!conn_state[i].in_use)
|
|
continue;
|
|
assert(conn_state[i].conn_sd > 2);
|
|
tmp_pollfd[j].fd = conn_state[i].conn_sd;
|
|
tmp_pollfd[j].events = POLLIN /* | POLLHUP | POLLNVAL */;
|
|
tmp_pollfd[j].revents = 0;
|
|
tmp_pollfd_to_conn_state[j] = i;
|
|
j++;
|
|
}
|
|
|
|
res = poll(tmp_pollfd, j, 20/*ms*/ /* 0=return immediately. */ );
|
|
if (res < 0) {
|
|
perror("poll(main) failed");
|
|
panic("poll(main) failed");
|
|
}
|
|
|
|
/* nothing happened. go round again. */
|
|
if (res == 0) {
|
|
continue;
|
|
}
|
|
|
|
/* inspect the fds. */
|
|
for (i = 0; i < j; i++) {
|
|
|
|
if (tmp_pollfd[i].revents & POLLIN) {
|
|
/* We have some activity on tmp_pollfd[i]. We need to
|
|
figure out which conn_state[] entry that corresponds
|
|
to, which is what tmp_pollfd_to_conn_state is for. */
|
|
Int conn_no = tmp_pollfd_to_conn_state[i];
|
|
Bool finished = handle_transaction(conn_no);
|
|
if (finished) {
|
|
/* this connection has been closed or otherwise gone
|
|
bad; forget about it. */
|
|
conn_count--;
|
|
fflush(stdout);
|
|
if (conn_count == 0 && exit_when_zero) {
|
|
if (0) printf("\n");
|
|
fflush(stdout);
|
|
exit_routine();
|
|
}
|
|
} else {
|
|
// maybe show stats
|
|
if (conn_state[i].stats_n_rdok_frames > 0
|
|
&& (conn_state[i].stats_n_rdok_frames % 1000) == 0) {
|
|
printf("(%d) SessionID %llu: sent %llu frames, "
|
|
"%llu MB (unz), %llu MB (z)\n",
|
|
conn_count, conn_state[conn_no].session_id,
|
|
conn_state[conn_no].stats_n_rdok_frames,
|
|
conn_state[conn_no].stats_n_read_unz_bytes / 1000000,
|
|
conn_state[conn_no].stats_n_read_z_bytes / 1000000);
|
|
fflush(stdout);
|
|
}
|
|
}
|
|
}
|
|
|
|
} /* for (i = 0; i < j; i++) */
|
|
|
|
do_snooze = False;
|
|
|
|
} /* while (1) */
|
|
|
|
/* NOTREACHED */
|
|
}
|
|
|
|
////////////////////////////////////////////////////
|
|
#include "../coregrind/m_debuginfo/minilzo-inl.c"
|
|
|
|
/*--------------------------------------------------------------------*/
|
|
/*--- end valgrind-di-server.c ---*/
|
|
/*--------------------------------------------------------------------*/
|