utils.c 23.7 KB
Newer Older
Leigh B. Stoller's avatar
Leigh B. Stoller committed
1
/*
2
 * Copyright (c) 2000-2017 University of Utah and the Flux Group.
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
 * 
 * {{{EMULAB-LICENSE
 * 
 * This file is part of the Emulab network testbed software.
 * 
 * This file is free software: you can redistribute it and/or modify it
 * under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or (at
 * your option) any later version.
 * 
 * This file 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 Affero General Public
 * License for more details.
 * 
 * You should have received a copy of the GNU Affero General Public License
 * along with this file.  If not, see <http://www.gnu.org/licenses/>.
 * 
 * }}}
Leigh B. Stoller's avatar
Leigh B. Stoller committed
22 23
 */

24 25 26 27 28 29 30 31 32
/*
 * Some simple common utility functions.
 */

#include <sys/types.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
33
#include <string.h>
34 35
#include <errno.h>
#include <sys/time.h>
Mike Hibler's avatar
Mike Hibler committed
36
#if !defined(linux) && !defined(__CYGWIN__)
37
#include <sys/sysctl.h>
38 39
#elif defined(linux)
#include <time.h>
Mike Hibler's avatar
Mike Hibler committed
40
#endif
Mike Hibler's avatar
Mike Hibler committed
41 42
#include <assert.h>

43
#include "decls.h"
Mike Hibler's avatar
Mike Hibler committed
44
#include "utils.h"
45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62

/*
 * Return current time in a string printable format. Caller must absorb
 * the string. 
 */
char *
CurrentTimeString(void)
{
	static char	buf[32];
	time_t		curtime;
	static struct tm tm;
	
	time(&curtime);
	strftime(buf, sizeof(buf), "%T", localtime_r(&curtime, &tm));

	return buf;
}

Mike Hibler's avatar
Mike Hibler committed
63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
/*
 * Determine a sleep time based on the resolution of the clock.
 * If doround is set, the provided value is rounded up to the next multiple
 * of the clock resolution.  If not, the value is returned untouched but
 * an optional warning is printed.
 *
 * Note that rounding is generally a bad idea.  Say the kernel is 1 usec
 * past system tick N (1 tick == 1000usec).  If we round a value of 1/2
 * tick up to 1 tick and then call sleep, the kernel will add our 1 tick
 * to the current value to get a time slightly past tick N+1.  It will then
 * round that up to N+2, so we effectively sleep almost two full ticks.
 * But if we don't round the tick, then adding that to the current time
 * might yield a value less than N+1, which will round up to N+1 and we
 * will at worst sleep one full tick.
 */
78
int
Mike Hibler's avatar
Mike Hibler committed
79
sleeptime(unsigned int usecs, char *str, int doround)
80 81 82 83 84
{
	static unsigned int clockres_us;
	int nusecs;

	if (clockres_us == 0) {
Mike Hibler's avatar
Mike Hibler committed
85
#if !defined(linux) && !defined(__CYGWIN__)
86
		struct clockinfo ci;
Mike Hibler's avatar
Mike Hibler committed
87
		size_t cisize = sizeof(ci);
88 89

		ci.hz = 0;
90 91 92 93
		if (sysctlbyname("kern.clockrate", &ci, &cisize, 0, 0) == 0 &&
		    ci.hz > 0)
			clockres_us = ci.tick;
		else
94 95 96 97 98 99 100 101
#elif defined(linux)
		struct timespec res;

		if (clock_getres(CLOCK_REALTIME, &res) == 0 && res.tv_sec == 0) {
			/* Assume min resolution of 1000 usec, round to nearest usec */
			if (res.tv_nsec < 1000000)
				clockres_us = 1000;
			else
102
				clockres_us = (res.tv_nsec / 1000000) * 1000;
103 104
		}
		else
105 106
#endif
		{
107
			FrisWarning("cannot get clock resolution, assuming 100HZ");
108
			clockres_us = 10000;
109 110
		}

111
		if (debug)
112
			FrisLog("clock resolution: %d us", clockres_us);
113 114
	}
	nusecs = ((usecs + clockres_us - 1) / clockres_us) * clockres_us;
Mike Hibler's avatar
Mike Hibler committed
115 116
	if (doround) {
		if (nusecs != usecs && str != NULL)
117 118 119
			FrisWarning("%s: rounded to %d from %d "
				    "due to clock resolution",
				    str, nusecs, usecs);
Mike Hibler's avatar
Mike Hibler committed
120 121
	} else {
		if (nusecs != usecs && str != NULL) {
122 123 124
			FrisWarning("%s: may be up to %d (instead of %d) "
				    "due to clock resolution",
				    str, nusecs, usecs);
Mike Hibler's avatar
Mike Hibler committed
125 126
		}
		nusecs = usecs;
127 128 129 130 131
	}

	return nusecs;
}

132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157
/*
 * Sleep. Basically wraps nanosleep, but since the threads package uses
 * signals, it keeps ending early!
 */
int
fsleep(unsigned int usecs)
{
	struct timespec time_to_sleep, time_not_slept;
	int	foo;

	time_to_sleep.tv_nsec  = (usecs % 1000000) * 1000;
	time_to_sleep.tv_sec   = usecs / 1000000;
	time_not_slept.tv_nsec = 0;
	time_not_slept.tv_sec  = 0;

	while ((foo = nanosleep(&time_to_sleep, &time_not_slept)) != 0) {
		if (foo < 0 && errno != EINTR) {
			return -1;
		}
		time_to_sleep.tv_nsec  = time_not_slept.tv_nsec;
		time_to_sleep.tv_sec   = time_not_slept.tv_sec;
		time_not_slept.tv_nsec = 0;
		time_not_slept.tv_sec  = 0;
	}
	return 0;
}
158

Mike Hibler's avatar
Mike Hibler committed
159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176
/*
 * Sleep til indicated time.
 * Returns 0 if it did not sleep.
 */
int
sleeptil(struct timeval *nexttime)
{
	struct timeval curtime;
	struct timespec todo, left;

	gettimeofday(&curtime, 0);
	if (!pasttime(&curtime, nexttime)) {
		subtime(&curtime, nexttime, &curtime);
		todo.tv_sec = curtime.tv_sec;
		todo.tv_nsec = curtime.tv_usec * 1000;
		left.tv_sec = left.tv_nsec = 0;
		while (nanosleep(&todo, &left) != 0) {
			if (errno != EINTR) {
177
				FrisLog("nanosleep failed\n");
Mike Hibler's avatar
Mike Hibler committed
178 179 180 181 182 183 184 185 186 187
				exit(1);
			}
			todo = left;
			left.tv_sec = left.tv_nsec = 0;
		}
		return 1;
	}
	return 0;
}

188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274
/*
 * Deal with variable chunk/block sizes
 */
static int _ChunkSize, _BlockSize;
static int _TotalChunks, _TotalBlocks;
static int _LastChunkSize, _LastBlockSize;

void
InitSizes(int32_t chunksize, int32_t blocksize, int64_t bytes)
{
	/* XXX no support for multiple chunk/block sizes yet */
	assert(chunksize == MAXCHUNKSIZE);
	assert(blocksize == MAXBLOCKSIZE);

	_ChunkSize = chunksize;
	_BlockSize = blocksize;
	_TotalBlocks = (bytes + _BlockSize-1) / _BlockSize;
	_TotalChunks = (_TotalBlocks + _ChunkSize-1) / _ChunkSize;

	/* how many bytes in last block (zero means full) */
	_LastBlockSize = bytes % _BlockSize;
	/* how many blocks in last chunk (zero means full) */
	_LastChunkSize = _TotalBlocks % _ChunkSize;
}
 
int
TotalChunks(void)
{
	return _TotalChunks;
}

/*
 * Return the size of a specific chunk.
 * Always the image chunk size (_ChunkSize) except possibly for a
 * final partial chunk.
 * If called with -1, return the image chunk size.
 */
int
ChunkSize(int chunkno)
{
	assert(chunkno < _TotalChunks);

	if (chunkno < _TotalChunks-1 || _LastChunkSize == 0)
		return _ChunkSize;
	return _LastChunkSize;
}

/*
 * Return the number of bytes in the indicated chunk.
 * Always the image chunk size in bytes (_ChunkSize * _BlockSize) except
 * possibly for a final partial chunk.
 */
int
ChunkBytes(int chunkno)
{
	assert(chunkno < _TotalChunks);

	if (chunkno < _TotalChunks-1 || _LastChunkSize == 0)
		return (_ChunkSize * _BlockSize);
	return ((_LastChunkSize-1) * _BlockSize + _LastBlockSize);
}

int
TotalBlocks(void)
{
	return _TotalBlocks;
}

/*
 * Return the size of a specific block.
 * Always the image block size (_BlockSize) except possibly for a
 * final partial block in the final chunk.
 * If called with -1, return the image block size.
 */
int
BlockSize(int chunkno, int blockno)
{
	int imageblock;

	assert(chunkno < _TotalChunks && blockno < _BlockSize);

	imageblock = chunkno * _ChunkSize + blockno;
	if (chunkno < 0 || imageblock < _TotalBlocks-1 || _LastBlockSize == 0)
		return _BlockSize;
	return _LastBlockSize;
}

Mike Hibler's avatar
Mike Hibler committed
275 276 277 278 279
void
BlockMapInit(BlockMap_t *blockmap, int block, int count)
{
	assert(block >= 0);
	assert(count > 0);
280 281
	assert(block < MAXCHUNKSIZE);
	assert(block + count <= MAXCHUNKSIZE);
Mike Hibler's avatar
Mike Hibler committed
282

283
	if (count == MAXCHUNKSIZE) {
Mike Hibler's avatar
Mike Hibler committed
284 285 286 287 288 289 290 291 292 293 294 295 296 297
		memset(blockmap->map, ~0, sizeof(blockmap->map));
		return;
	}
	memset(blockmap->map, 0, sizeof(blockmap->map));
	BlockMapAdd(blockmap, block, count);
}

void
BlockMapAdd(BlockMap_t *blockmap, int block, int count)
{
	int i, off;

	assert(block >= 0);
	assert(count > 0);
298 299
	assert(block < MAXCHUNKSIZE);
	assert(block + count <= MAXCHUNKSIZE);
Mike Hibler's avatar
Mike Hibler committed
300 301 302

	i = block / CHAR_BIT;
	off = block % CHAR_BIT;
Mike Hibler's avatar
Mike Hibler committed
303 304 305 306 307 308 309 310 311 312 313
	while (count > 0) {
		if (off == 0 && count >= CHAR_BIT) {
			blockmap->map[i++] = ~0;
			count -= CHAR_BIT;
		} else {
			blockmap->map[i] |= (1 << off);
			if (++off == CHAR_BIT) {
				i++;
				off = 0;
			}
			count--;
Mike Hibler's avatar
Mike Hibler committed
314 315 316 317
		}
	}
}

318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344
void
BlockMapClear(BlockMap_t *blockmap, int block, int count)
{
	int i, off;

	assert(block >= 0);
	assert(count > 0);
	assert(block < MAXCHUNKSIZE);
	assert(block + count <= MAXCHUNKSIZE);

	i = block / CHAR_BIT;
	off = block % CHAR_BIT;
	while (count > 0) {
		if (off == 0 && count >= CHAR_BIT) {
			blockmap->map[i++] = 0;
			count -= CHAR_BIT;
		} else {
			blockmap->map[i] &= ~(1 << off);
			if (++off == CHAR_BIT) {
				i++;
				off = 0;
			}
			count--;
		}
	}
}

Mike Hibler's avatar
Mike Hibler committed
345 346 347 348 349 350 351 352 353
/*
 * Mark the specified block as allocated and return the old value
 */
int
BlockMapAlloc(BlockMap_t *blockmap, int block)
{
	int i, off;

	assert(block >= 0);
354
	assert(block < MAXCHUNKSIZE);
Mike Hibler's avatar
Mike Hibler committed
355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418

	i = block / CHAR_BIT;
	off = block % CHAR_BIT;
	if ((blockmap->map[i] & (1 << off)) == 0) {
		blockmap->map[i] |= (1 << off);
		return 0;
	}
	return 1;
}

/*
 * Extract the next contiguous range of blocks, removing them from the map.
 * Returns the number of blocks extracted.
 */
int
BlockMapExtract(BlockMap_t *blockmap, int *blockp)
{
	int block, count, mask;
	int i, bit;

	assert(blockp != 0);

	/*
	 * Skip empty space at the front quickly
	 */
	for (i = 0; i < sizeof(blockmap->map); i++)
		if (blockmap->map[i] != 0)
			break;

	for (block = count = 0; i < sizeof(blockmap->map); i++) {
		for (bit = 0; bit < CHAR_BIT; bit++) {
			mask = 1 << bit;
			if ((blockmap->map[i] & mask) != 0) {
				blockmap->map[i] &= ~mask;
				if (count == 0)
					block = (i * CHAR_BIT) + bit;
				count++;
			} else {
				if (count > 0) {
					*blockp = block;
					return count;
				}
				if (blockmap->map[i] == 0)
					break;
			}
		}
	}
	if (count > 0)
		*blockp = block;

	return count;
}

/*
 * Return the number of blocks allocated in the range specified
 */
int
BlockMapIsAlloc(BlockMap_t *blockmap, int block, int count)
{
	int i, off, did = 0;
	char val;

	assert(block >= 0);
	assert(count > 0);
419 420
	assert(block < MAXCHUNKSIZE);
	assert(block + count <= MAXCHUNKSIZE);
Mike Hibler's avatar
Mike Hibler committed
421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456

	i = block / CHAR_BIT;
	off = block % CHAR_BIT;
	val = blockmap->map[i];
	while (count > 0) {
		/*
		 * Handle common aggregate cases
		 */
		if (off == 0 && count >= CHAR_BIT && (val == 0 || val == ~0)) {
			if (val)
				did += CHAR_BIT;
			count -= CHAR_BIT;
			off += CHAR_BIT;
		} else {
			if (val & (1 << off))
				did++;
			count--;
			off++;
		}
		if (off == CHAR_BIT && count > 0) {
			val = blockmap->map[++i];
			off = 0;
		}
	}
	return did;
}

void
BlockMapInvert(BlockMap_t *oldmap, BlockMap_t *newmap)
{
	int i;

	for (i = 0; i < sizeof(oldmap->map); i++)
		newmap->map[i] = ~(oldmap->map[i]);
}

Mike Hibler's avatar
Mike Hibler committed
457 458 459
/*
 * Merge any blocks from request frommap into tomap.
 */
Mike Hibler's avatar
Mike Hibler committed
460 461 462 463 464 465
int
BlockMapMerge(BlockMap_t *frommap, BlockMap_t *tomap)
{
	int i, bit, mask, did = 0;

	for (i = 0; i < sizeof(frommap->map); i++) {
Mike Hibler's avatar
Mike Hibler committed
466 467
		if (frommap->map[i] == 0 || tomap->map[i] == ~0 ||
		    frommap->map[i] == tomap->map[i])
Mike Hibler's avatar
Mike Hibler committed
468 469 470 471 472 473 474 475 476 477 478 479 480 481
			continue;
		for (bit = 0; bit < CHAR_BIT; bit++) {
			mask = 1 << bit;
			if ((frommap->map[i] & mask) != 0 &&
			    (tomap->map[i] & mask) == 0) {
				tomap->map[i] |= mask;
				did++;
			}
		}
	}

	return did;
}

Mike Hibler's avatar
Mike Hibler committed
482 483
/*
 * Return the block number of the first block allocated in the map.
484
 * Returns MAXCHUNKSIZE if no block is set.
Mike Hibler's avatar
Mike Hibler committed
485
 */
Mike Hibler's avatar
Mike Hibler committed
486 487 488
int
BlockMapFirst(BlockMap_t *blockmap)
{
Mike Hibler's avatar
Mike Hibler committed
489 490
	int block, i;

491
	assert(sizeof(blockmap->map) * CHAR_BIT == MAXCHUNKSIZE);
Mike Hibler's avatar
Mike Hibler committed
492 493 494 495 496 497 498

	/*
	 * Skip empty space at the front quickly
	 */
	for (i = 0; i < sizeof(blockmap->map); i++)
		if (blockmap->map[i] != 0)
			break;
Mike Hibler's avatar
Mike Hibler committed
499

Mike Hibler's avatar
Mike Hibler committed
500 501 502 503 504 505 506 507 508 509
	block = i * CHAR_BIT;
	if (i < sizeof(blockmap->map)) {
		int bit, mask;
		for (bit = 0; bit < CHAR_BIT; bit++) {
			mask = 1 << bit;
			if ((blockmap->map[i] & mask) != 0)
				return (block + bit);
		}
	}
	return block;
Mike Hibler's avatar
Mike Hibler committed
510 511 512 513
}

/*
 * Repeatedly apply the given function to each contiguous allocated range.
Mike Hibler's avatar
Mike Hibler committed
514 515 516
 * If the function returns non-zero, we stop processing at that point and
 * return.  This allows a "short-circuit" ability.
 *
Mike Hibler's avatar
Mike Hibler committed
517 518 519 520
 * Returns number of allocated blocks processed.
 */
int
BlockMapApply(BlockMap_t *blockmap, int chunk,
Mike Hibler's avatar
Mike Hibler committed
521
	      int (*func)(int, int, int, void *), void *arg)
Mike Hibler's avatar
Mike Hibler committed
522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538
{
	int block, count, mask;
	int i, bit, val;
	int did = 0;

	block = count = 0;
	for (i = 0; i < sizeof(blockmap->map); i++) {
		val = blockmap->map[i];
		for (bit = 0; bit < CHAR_BIT; bit++) {
			mask = 1 << bit;
			if ((val & mask) != 0) {
				val &= ~mask;
				if (count == 0)
					block = (i * CHAR_BIT) + bit;
				count++;
			} else {
				if (count > 0) {
Mike Hibler's avatar
Mike Hibler committed
539 540 541
					if (func &&
					    func(chunk, block, count, arg))
						return(did+count);
Mike Hibler's avatar
Mike Hibler committed
542 543 544 545 546 547 548 549 550
					did += count;
					count = 0;
				}
				if (val == 0)
					break;
			}
		}
	}
	if (count > 0) {
Mike Hibler's avatar
Mike Hibler committed
551 552
		if (func && func(chunk, block, count, arg))
			return (did+count);
Mike Hibler's avatar
Mike Hibler committed
553 554 555 556 557 558
		did += count;
	}

	return did;
}

559 560 561 562 563 564 565 566 567 568 569 570 571 572 573
#ifdef STATS
void
ClientStatsDump(unsigned int id, ClientStats_t *stats)
{
	int seconds;

	switch (stats->version) {
	case 1:
		/* compensate for start delay */
		stats->u.v1.runmsec -= stats->u.v1.delayms;
		while (stats->u.v1.runmsec < 0) {
			stats->u.v1.runsec--;
			stats->u.v1.runmsec += 1000;
		}

574 575 576 577 578
		FrisLog("Client %u Performance:", id);
		FrisLog("  runtime:                %d.%03d sec",
			stats->u.v1.runsec, stats->u.v1.runmsec);
		FrisLog("  start delay:            %d.%03d sec",
			stats->u.v1.delayms/1000, stats->u.v1.delayms%1000);
579 580 581
		seconds = stats->u.v1.runsec;
		if (seconds == 0)
			seconds = 1;
582 583 584 585 586 587 588 589 590 591 592
		FrisLog("  real data written:      %qu (%qu Bps)",
			stats->u.v1.rbyteswritten,
			stats->u.v1.rbyteswritten/seconds);
		FrisLog("  effective data written: %qu (%qu Bps)",
			stats->u.v1.ebyteswritten,
			stats->u.v1.ebyteswritten/seconds);
		FrisLog("Client %u Params:", id);
		FrisLog("  chunk/block size:     %d/%d",
			CHUNKSIZE, BLOCKSIZE);
		FrisLog("  chunk buffers:        %d",
			stats->u.v1.chunkbufs);
Mike Hibler's avatar
Mike Hibler committed
593
		if (stats->u.v1.writebufmem)
594
			FrisLog("  disk buffering (MB):  %d",
595
				stats->u.v1.writebufmem);
596 597
		FrisLog("  sockbuf size (KB):    %d",
			sockbufsize/1024);
598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624
		FrisLog("  readahead/inprogress: %d/%d",
			stats->u.v1.maxreadahead, stats->u.v1.maxinprogress);
		FrisLog("  recv timo/count:      %d/%d",
			stats->u.v1.pkttimeout, stats->u.v1.idletimer);
		FrisLog("  re-request delay:     %d",
			stats->u.v1.redodelay);
		FrisLog("  writer idle delay:    %d",
			stats->u.v1.idledelay);
		FrisLog("  randomize requests:   %d",
			stats->u.v1.randomize);
		FrisLog("Client %u Stats:", id);
		FrisLog("  net thread idle/blocked:        %d/%d",
			stats->u.v1.recvidles, stats->u.v1.nofreechunks);
		FrisLog("  decompress thread idle/blocked: %d/%d",
			stats->u.v1.nochunksready, stats->u.v1.decompblocks);
		FrisLog("  disk thread idle:        %d",
			stats->u.v1.writeridles);
		FrisLog("  join/request msgs:       %d/%d",
			stats->u.v1.joinattempts, stats->u.v1.requests);
		FrisLog("  dupblocks(chunk done):   %d",
			stats->u.v1.dupchunk);
		FrisLog("  dupblocks(in progress):  %d",
			stats->u.v1.dupblock);
		FrisLog("  partial requests/blocks: %d/%d",
			stats->u.v1.prequests, stats->u.v1.lostblocks);
		FrisLog("  re-requests:             %d",
			stats->u.v1.rerequests);
625 626
		FrisLog("  full chunk re-requests:  %d",
			stats->u.v1.fullrerequests);
627 628
		FrisLog("  partially-filled drops:  %d",
			stats->u.v1.partialdrops);
629 630 631
		break;

	default:
632
		FrisLog("Unknown stats version %d", stats->version);
633 634 635 636
		break;
	}
}
#endif
637

638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748
#ifdef EMULAB_EVENTS
static int
split(char *str, char *elems[], int nelems)
{
	char *token;
	int i;

	if (nelems <= 0 || strlen(str) == 0)
		return 0;
	i = 0;
	while ((token = strsep(&str, "/")) != NULL) {
		if (token[0] == '\0')
			continue;
		elems[i] = token;
		if (++i == nelems)
			break;
	}

	return i;
}

/*
 * This is a big horrible hack to convert an image pathname back into
 * an Emulab imageid of the form: <pid>/<imageid>[:<version>][,<sig>]
 * It is ifdef'ed under EMULAB_EVENTS because we only use it in the
 * heartbeat event reporting code.
 *
 * We recognize five styles of image paths:
 *
 * 1. /usr/testbed/images/<blah>
 *    A standard image where pid will be 'emulab-ops' and the rest in <blah>
 * 2. /usr/testbed/images/<imageid>/<blah>
 *    A "directory image" style standard image.
 * 3. /proj/<pid>/images/<blah>
 *    A user image where pid is explicit and the rest in <blah>
 * 4. /proj/<pid>/images/<imageid>/<blah>
 *    A user image in "directory image" format
 * 5. /z/image_cache/<pid>/<imageid>[:<version>][,sig]
 *    A subboss cached image. pid and imageid are explicit!
 *
 * For #1-4 <blah> will be of the form:
 *    <imageid>.ndz[:<version>][.sig]
 *
 * Anything that does not start with a '/' is assumed to already be an
 * Emulab imageid.
 *
 * Returns a dynamically allocated string containing the Emulab imageid
 * (which should be freed by the caller) or "UNKNOWN/UNKNOWN" if the string
 * is unparsable by our rules.
 */
char *
extract_imageid(char *opath)
{
	static char std_prefix[] = "/usr/testbed/images";
	static char usr_prefix[] = "/proj/";
	static char subboss_prefix[] = "/z/image_cache/";
	char *buf, *path, *comps[8];
	char *pid, *imageid, *vers, *sig;
	int ncomps, len;

	path = strdup(opath);
	if (path[0] != '/')
		return path;

	pid = imageid = "UNKNOWN";
	vers = sig = "";

	/* XXX see if it is a signature file (ends in .sig) */
	sig = strstr(path, ".sig");
	if (sig == NULL || sig[4] != '\0')
		sig = "";
	else {
		sig[0] = '\0';
		sig = ",sig";
	}

	ncomps = split(path, comps, 8);

	/* #5: /z/image_cache/<pid>/<imageid>[,sig] */
	if (strncmp(opath, subboss_prefix, strlen(subboss_prefix)) == 0 &&
	    ncomps == 4) {
		pid = comps[2];
		imageid = comps[3];
		vers = sig = "";
	}
	else {
		/* for the remaining cases, determine pid and "blah" */
		char *blah = NULL;

		if (strncmp(opath, std_prefix, strlen(std_prefix)) == 0) {
			pid = "emulab-ops";

			/* #1: /usr/testbed/images/<blah> */
			if (ncomps == 4)
				blah = comps[3];
			/* #2: /usr/testbed/images/<imageid>/blah */
			else if (ncomps == 5)
				blah = comps[4];
		} else if (strncmp(opath, usr_prefix, strlen(usr_prefix)) == 0) {
			/* #3: /proj/<pid>/images/<blah> */
			if (ncomps == 4) {
				pid = comps[1];
				blah = comps[3];
			}
			/* #3: /proj/<pid>/images/<imageid>/<blah> */
			else if (ncomps == 5) {
				pid = comps[1];
				blah = comps[4];
			}
		}

Mike Hibler's avatar
Mike Hibler committed
749
		/* blah: <imageid>.{ddz,ndz}[:<version>] */
750 751
		if (blah) {
			imageid = blah;
Mike Hibler's avatar
Mike Hibler committed
752 753
			if ((vers = strstr(blah, ".ndz")) != NULL ||
			    (vers = strstr(blah, ".ddz")) != NULL) {
754 755 756 757 758 759 760 761 762 763 764 765 766 767 768
				vers[0] = '\0';
				vers += 4;
			}
		}
	}

	len = strlen(pid) + strlen(imageid) + strlen(vers) + strlen(sig) + 2;
	if ((buf = malloc(len)) != NULL)
		sprintf(buf, "%s/%s%s%s", pid, imageid, vers, sig);

	free(path);
	return buf;
}
#endif

769 770 771 772 773 774 775 776 777
#ifdef MASTER_SERVER
#include "configdefs.h"

char *
GetMSError(int error)
{
	char *err;

	switch (error) {
778 779 780
	case 0:
		err = "no error";
		break;
781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798
	case MS_ERROR_FAILED:
		err = "server authentication error";
		break;
	case MS_ERROR_NOHOST:
		err = "unknown host";
		break;
	case MS_ERROR_NOIMAGE:
		err = "unknown image";
		break;
	case MS_ERROR_NOACCESS:
		err = "access not allowed";
		break;
	case MS_ERROR_NOMETHOD:
		err = "not available via specified method";
		break;
	case MS_ERROR_INVALID:
		err = "invalid argument";
		break;
799 800 801
	case MS_ERROR_TRYAGAIN:
		err = "image busy, try again later";
		break;
802 803 804 805 806 807 808 809 810
	case MS_ERROR_TOOBIG:
		err = "upload too large for target image";
		break;
	case MS_ERROR_BADMTIME:
		err = "bad image modification time";
		break;
	case MS_ERROR_NOTIMPL:
		err = "operation not implemented";
		break;
811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845
	default:
		err = "unknown error";
		break;
	}

	return err;
}

char *
GetMSMethods(int methods)
{
	static char mbuf[256];

	mbuf[0] = '\0';
	if (methods & MS_METHOD_UNICAST) {
		if (mbuf[0] != '\0')
			strcat(mbuf, "/");
		strcat(mbuf, "unicast");
	}
	if (methods & MS_METHOD_MULTICAST) {
		if (mbuf[0] != '\0')
			strcat(mbuf, "/");
		strcat(mbuf, "multicast");
	}
	if (methods & MS_METHOD_BROADCAST) {
		if (mbuf[0] != '\0')
			strcat(mbuf, "/");
		strcat(mbuf, "broadcast");
	}
	if (mbuf[0] == '\0')
		strcat(mbuf, "UNKNOWN");

	return mbuf;
}

846 847 848 849 850
/*
 * Print the contents of a GET reply to stdout.
 * The reply struct fields should be in HOST order; i.e., as returned
 * by ClientNetFindServer.
 */
851
void
852
PrintGetInfo(char *imageid, GetReply *reply, int raw)
853
{
854 855 856 857 858 859 860 861 862 863 864 865
	struct in_addr in;
	uint64_t isize;
	int len;

	if (raw) {
		printf("imageid=%s\n", imageid);
		printf("error=%d\n", reply->error);
		if (reply->error)
			return;

		printf("method=0x%x\n", reply->method);
		isize = ((uint64_t)reply->hisize << 32) | reply->losize;
Mike Hibler's avatar
Mike Hibler committed
866
		printf("size=%llu\n", (unsigned long long)isize);
867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905
		printf("sigtype=0x%x\n", reply->sigtype);
		switch (reply->sigtype) {
		case MS_SIGTYPE_MTIME:
			printf("sig=0x%x\n", *(uint32_t *)reply->signature);
			len = 0;
			break;
		case MS_SIGTYPE_MD5:
			len = 16;
			break;
		case MS_SIGTYPE_SHA1:
			len = 20;
			break;
		default:
			len = 0;
			break;
		}
		if (len > 0) {
			char sigbuf[MS_MAXSIGLEN*2+1], *sbp;
			int i;

			sbp = sigbuf;
			for (i = 0; i < len; i++) {
				sprintf(sbp, "%02x", reply->signature[i]);
				sbp += 2;
			}
			*sbp = '\0';
			printf("sig=0x%s\n", sigbuf);
		}
		printf("running=%d\n", reply->isrunning);
		if (reply->isrunning) {
			in.s_addr = htonl(reply->servaddr);
			printf("servaddr=%s\n", inet_ntoa(in));
			in.s_addr = htonl(reply->addr);
			printf("addr=%s\n", inet_ntoa(in));
			printf("port=%d\n", reply->port);
		}
		return;
	}

906
	if (reply->error) {
907
		printf("%s: server denied access: %s\n",
908 909 910 911 912 913
		    imageid, GetMSError(reply->error));
		return;
	}

	if (reply->isrunning) {
		in.s_addr = htonl(reply->addr);
914
		printf("%s: access OK, server running at %s:%d using %s\n",
915 916 917
		    imageid, inet_ntoa(in), reply->port,
		    GetMSMethods(reply->method));
	} else
918
		printf("%s: access OK, available methods=%s\n",
919 920
		    imageid, GetMSMethods(reply->method));

921
	isize = ((uint64_t)reply->hisize << 32) | reply->losize;
Mike Hibler's avatar
Mike Hibler committed
922
	printf("  size=%llu\n", (unsigned long long)isize);
923

924 925 926
	switch (reply->sigtype) {
	case MS_SIGTYPE_MTIME:
	{
927 928
		time_t mt = *(time_t *)reply->signature;
		printf("  modtime=%s\n", ctime(&mt));
929 930 931 932 933
	}
	default:
		break;
	}
}
934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952

/*
 * Print the contents of a PUT reply to stdout.
 * The reply struct fields should be in HOST order.
 */
void
PrintPutInfo(char *imageid, PutReply *reply, int raw)
{
	struct in_addr in;
	uint64_t isize;
	int len;

	if (raw) {
		printf("imageid=%s\n", imageid);
		printf("error=%d\n", reply->error);
		if (reply->error) {
			if (reply->error == MS_ERROR_TOOBIG) {
				isize = ((uint64_t)reply->himaxsize << 32) |
					reply->lomaxsize;
Mike Hibler's avatar
Mike Hibler committed
953 954
				printf("maxsize=%llu\n",
				       (unsigned long long)isize);
955 956 957 958 959 960 961 962
			}
			return;
		}

		printf("exists=%d\n", reply->exists);
		if (reply->exists) {
			isize = ((uint64_t)reply->hisize << 32) |
				reply->losize;
Mike Hibler's avatar
Mike Hibler committed
963
			printf("size=%llu\n", (unsigned long long)isize);
964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995
			printf("sigtype=0x%x\n", reply->sigtype);
			switch (reply->sigtype) {
			case MS_SIGTYPE_MTIME:
				printf("sig=0x%x\n",
				       *(uint32_t *)reply->signature);
				len = 0;
				break;
			case MS_SIGTYPE_MD5:
				len = 16;
				break;
			case MS_SIGTYPE_SHA1:
				len = 20;
				break;
			default:
				len = 0;
				break;
			}
			if (len > 0) {
				char sigbuf[MS_MAXSIGLEN*2+1], *sbp;
				int i;

				sbp = sigbuf;
				for (i = 0; i < len; i++) {
					sprintf(sbp, "%02x",
						reply->signature[i]);
					sbp += 2;
				}
				*sbp = '\0';
				printf("sig=0x%s\n", sigbuf);
			}
		}
		isize = ((uint64_t)reply->himaxsize << 32) | reply->lomaxsize;
Mike Hibler's avatar
Mike Hibler committed
996
		printf("maxsize=%llu\n", (unsigned long long)isize);
997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010
		in.s_addr = htonl(reply->addr);
		printf("servaddr=%s\n", inet_ntoa(in));
		in.s_addr = htonl(reply->addr);
		printf("addr=%s\n", inet_ntoa(in));
		printf("port=%d\n", reply->port);
		return;
	}

	if (reply->error) {
		printf("%s: server denied access: %s\n",
		    imageid, GetMSError(reply->error));
		if (reply->error == MS_ERROR_TOOBIG) {
			isize = ((uint64_t)reply->himaxsize << 32) |
				reply->lomaxsize;
Mike Hibler's avatar
Mike Hibler committed
1011 1012
			printf("  max allowed size=%llu\n",
			       (unsigned long long)isize);
1013 1014 1015 1016 1017 1018 1019
		}
		return;
	}

	if (reply->exists) {
		printf("%s: currently exists\n", imageid);
		isize = ((uint64_t)reply->hisize << 32) | reply->losize;
Mike Hibler's avatar
Mike Hibler committed
1020
		printf("  size=%llu\n", (unsigned long long)isize);
1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031
		switch (reply->sigtype) {
		case MS_SIGTYPE_MTIME:
		{
			time_t mt = *(time_t *)reply->signature;
			printf("  modtime=%s\n", ctime(&mt));
		}
		default:
			break;
		}
	}
	isize = ((uint64_t)reply->himaxsize << 32) | reply->lomaxsize;
Mike Hibler's avatar
Mike Hibler committed
1032
	printf("  max allowed size=%llu\n", (unsigned long long)isize);
1033 1034
	in.s_addr = htonl(reply->addr);
	printf("%s: access OK, upload via %s:%d, maxsize=%llu\n",
Mike Hibler's avatar
Mike Hibler committed
1035 1036
	       imageid, inet_ntoa(in), reply->port,
	       (unsigned long long)isize);
1037
}
1038
#endif