bootinfo_mysql.c 16.9 KB
Newer Older
Leigh Stoller's avatar
Leigh Stoller committed
1 2
/*
 * EMULAB-COPYRIGHT
3
 * Copyright (c) 2000-2009 University of Utah and the Flux Group.
Leigh Stoller's avatar
Leigh Stoller committed
4 5 6
 * All rights reserved.
 */

7 8
#include <sys/types.h>
#include <netinet/in.h>
9
#include <netdb.h>
10
#include <stdio.h>
11
#include <stdlib.h>
12
#include <syslog.h>
13 14
#include <arpa/inet.h>
#include <string.h>
15
#include "config.h"
16 17
#include "log.h"
#include "tbdb.h"
18
#include "bootwhat.h"
19
#include "bootinfo.h"
20 21
#include <mysql/mysql.h>

22
/* XXX Should be configured in */
23
#define NEWNODEPID      "emulab-ops"
24 25
#define NEWNODEOSID	"NEWNODE-MFS"

26 27
#ifdef USE_MYSQL_DB

28
static int parse_multiboot_path(char *path, boot_what_t *info);
29
static void parse_mfs_path(char *path, boot_what_t *info);
30
static int boot_newnode_mfs(struct in_addr, int, boot_what_t *);
31 32 33 34

int
open_bootinfo_db(void)
{
35 36 37
	if (!dbinit())
		return 1;

38 39 40
	return 0;
}

41 42 43 44 45 46 47 48
/*
  WARNING!!!
  
  DO NOT change this function without making corresponding changes to
  the perl version of this code in db/libdb.pm . They MUST ALWAYS
  return exactly the same result given the same inputs.
*/

49
int
50 51
query_bootinfo_db(struct in_addr ipaddr, char *node_id, int version, 
		  boot_what_t *info, char* key)
52
{
53
	int		nrows, rval = 0;
54 55
	MYSQL_RES	*res, *res2;
	MYSQL_ROW	row, row2;
56
	char		ipstr[32];
57
	int		haskey=0;
58
	int             bootdisk_bios_id = 0;
59

60 61 62 63 64 65
	char		savedkey[HOSTKEY_LENGTH];
	if(key != NULL)
		{
		strncpy(savedkey, key, HOSTKEY_LENGTH);
		haskey=1;
		}
66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
	info->cmdline[0] = 0;	/* Must zero first byte! */
	info->flags      = 0;
	strcpy(ipstr, inet_ntoa(ipaddr));

#define DEF_BOOT_OSID		0
#define DEF_BOOT_CMDLINE	1
#define DEF_BOOT_PATH		2
#define DEF_BOOT_MFS		3
#define DEF_BOOT_PARTITION	4
#define TEMP_BOOT_OSID		5
#define TEMP_BOOT_PATH		6
#define TEMP_BOOT_MFS		7
#define TEMP_BOOT_PARTITION	8
#define NEXT_BOOT_OSID		9
#define NEXT_BOOT_CMDLINE	10
#define NEXT_BOOT_PATH		11
#define NEXT_BOOT_MFS		12
#define NEXT_BOOT_PARTITION	13
#define PID			14
85
#define PXE_BOOT_PATH		15
86 87 88
#define DEFINED(x)		(row[(x)] != NULL && row[(x)][0] != '\0')
#define TOINT(x)		(atoi(row[(x)]))

89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128
	if (node_id) {
		/*
		 * Right now, this is ONLY used for checking bootinfo for
		 * imageable vnodes.  All bets off if you try it for something
		 * else!
		 */
		res = mydb_query("select n.def_boot_osid, n.def_boot_cmd_line, "
				 "        odef.path, odef.mfs, pdef.partition, "
				 "       n.temp_boot_osid, "
				 "        otemp.path, otemp.mfs, ptemp.partition, "
				 "       n.next_boot_osid, n.next_boot_cmd_line, "
				 "        onext.path, onext.mfs, pnext.partition, "
				 "       r.pid,n.pxe_boot_path "
				 " from nodes as n "
				 "left join reserved as r on n.node_id=r.node_id "
				 "left join partitions as pdef on "
				 "     n.node_id=pdef.node_id and "
				 "     n.def_boot_osid=pdef.osid "
				 "left join os_info as odef on "
				 "     odef.osid=n.def_boot_osid "
				 "left join partitions as ptemp on "
				 "     n.node_id=ptemp.node_id and "
				 "     n.temp_boot_osid=ptemp.osid "
				 "left join os_info as otemp on "
				 "     otemp.osid=n.temp_boot_osid "
				 "left join partitions as pnext on "
				 "     n.node_id=pnext.node_id and "
				 "     n.next_boot_osid=pnext.osid "
				 "left join os_info as onext on "
				 "     onext.osid=n.next_boot_osid "
				 "left outer join "
				 "  (select type,attrvalue from node_type_attributes "
				 "     where attrkey='nobootinfo' and attrvalue='1' "
				 "     group by type) as nobootinfo_types "
				 "  on n.type=nobootinfo_types.type "
				 "where n.node_id='%s' "
				 "  and nobootinfo_types.attrvalue is NULL",
				 16, node_id);
	}
	else if (! haskey) {
129 130 131 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 158 159 160 161
		res = mydb_query("select n.def_boot_osid, n.def_boot_cmd_line, "
				 "        odef.path, odef.mfs, pdef.partition, "
				 "       n.temp_boot_osid, "
				 "        otemp.path, otemp.mfs, ptemp.partition, "
				 "       n.next_boot_osid, n.next_boot_cmd_line, "
				 "        onext.path, onext.mfs, pnext.partition, "
				 "       r.pid,n.pxe_boot_path "
				 " from interfaces as i "
				 "left join nodes as n on i.node_id=n.node_id "
				 "left join reserved as r on i.node_id=r.node_id "
				 "left join partitions as pdef on "
				 "     n.node_id=pdef.node_id and "
				 "     n.def_boot_osid=pdef.osid "
				 "left join os_info as odef on "
				 "     odef.osid=n.def_boot_osid "
				 "left join partitions as ptemp on "
				 "     n.node_id=ptemp.node_id and "
				 "     n.temp_boot_osid=ptemp.osid "
				 "left join os_info as otemp on "
				 "     otemp.osid=n.temp_boot_osid "
				 "left join partitions as pnext on "
				 "     n.node_id=pnext.node_id and "
				 "     n.next_boot_osid=pnext.osid "
				 "left join os_info as onext on "
				 "     onext.osid=n.next_boot_osid "
				 "left outer join "
				 "  (select type,attrvalue from node_type_attributes "
				 "     where attrkey='nobootinfo' and attrvalue='1' "
				 "     group by type) as nobootinfo_types "
				 "  on n.type=nobootinfo_types.type "
				 "where i.IP='%s' "
				 "  and nobootinfo_types.attrvalue is NULL",
				 16, inet_ntoa(ipaddr));
162 163

		/* Get boot drive from DB */
164
		res2 = mydb_query("select attrvalue from node_attributes as a, nodes as n, interfaces as i where "
165 166 167 168 169 170 171 172 173
				  "i.IP = '%s' and i.node_id = n.node_id and n.type = a.type and a.attrkey = 'bootdisk_bios_id';",
				  1, inet_ntoa(ipaddr));

		if (!res2) {
			error("Query failed for host %s\n", node_id ? node_id : ipstr);
			/* XXX Wrong. Should fail so client can request again later */
			return 0;
		}

174 175 176 177 178 179 180 181 182 183 184 185 186
		if (!mysql_num_rows(res2)) {
			mysql_free_result(res2);
			res2 = mydb_query("select attrvalue from node_type_attributes as a, nodes as n, interfaces as i where "
					  "i.IP = '%s' and i.node_id = n.node_id and n.type = a.type and a.attrkey = 'bootdisk_bios_id';",
					  1, inet_ntoa(ipaddr));

			if (!res2) {
				error("Query failed for host %s\n", node_id ? node_id : ipstr);
				/* XXX Wrong. Should fail so client can request again later */
				return 0;
			}
		}

187 188 189 190
		if (mysql_num_rows(res2)) {
			row2 = mysql_fetch_row(res2);
			bootdisk_bios_id = atoi(row2[0]);
		}
191 192
		
		mysql_free_result(res2);
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
	}
	else { /* User provided a widearea hostkey, so they don't have a necessarily-unique IP address. */
		/* This is meant to be similar to the above, but queries on the wideareanodekey instead. */
		res = mydb_query("SELECT n.def_boot_osid, n.def_boot_cmd_line, "
				 "odef.path, odef.mfs, pdef.partition, "
				 "n.temp_boot_osid, "
				 "otemp.path, otemp.mfs, ptemp.partition, "
				 "n.next_boot_osid, n.next_boot_cmd_line, "
				 "onext.path, onext.mfs, pnext.partition, "
				 "r.pid,n.pxe_boot_path "
				 "FROM nodes AS n "
				 "LEFT JOIN reserved AS r ON n.node_id=r.node_id "
				 "LEFT JOIN partitions AS pdef ON n.node_id=pdef.node_id AND n.def_boot_osid=pdef.osid "
				 "LEFT JOIN os_info AS odef ON odef.osid=n.def_boot_osid "
				 "LEFT JOIN partitions AS ptemp ON n.node_id=ptemp.node_id AND n.temp_boot_osid=ptemp.osid "
				 "LEFT JOIN os_info AS otemp ON otemp.osid=n.temp_boot_osid "
				 "LEFT JOIN partitions AS pnext ON n.node_id=pnext.node_id AND n.next_boot_osid=pnext.osid "
				 "LEFT JOIN os_info AS onext ON onext.osid=n.next_boot_osid "
				 "LEFT OUTER JOIN "
					"(SELECT type,attrvalue FROM node_type_attributes WHERE attrkey='nobootinfo' AND attrvalue='1' GROUP BY type) "
				 	"AS nobootinfo_types ON n.type=nobootinfo_types.type "
				 "WHERE n.node_id IN "
					"(SELECT node_id FROM widearea_nodeinfo WHERE privkey='%s') "
					"AND nobootinfo_types.attrvalue IS NULL;", 16, savedkey);
	}
	
219
	if (!res) {
220
		error("Query failed for host %s\n", node_id ? node_id : ipstr);
221 222
		/* XXX Wrong. Should fail so client can request again later */
		return 0;
223
	}
224
	nrows = mysql_num_rows(res);
225 226 227 228

	switch (nrows) {
	case 0:
		mysql_free_result(res);
229
		return boot_newnode_mfs(ipaddr, version, info);
230 231 232
	case 1:
		break;
	default:
233 234
		error("%d entries for host %s\n",
		      nrows, node_id ? node_id : ipstr);
235 236 237
		break;
	}
	row = mysql_fetch_row(res);
Mike Hibler's avatar
Mike Hibler committed
238 239

	/*
240 241
	 * Version >=1 supports wait if not allocated. Client will recontact
	 * us later.
Mike Hibler's avatar
Mike Hibler committed
242
	 */
243 244 245
	if (version >= 1 && row[PID] == (char *) NULL) {
		info->type = BIBOOTWHAT_TYPE_WAIT;
		goto done;
Mike Hibler's avatar
Mike Hibler committed
246 247
	}

248 249
	/*
	 * If we received a query from a node whose PXE boot program is
250 251 252
	 * not an "Emulab pxeboot", then the node may be coming out of PXEWAIT
	 * and we need to tell it to reboot again to pick up the new PXE boot
	 * program.  An "Emulab pxeboot" is one that speaks bootinfo.
253
	 *
254 255 256
	 * XXX note that an "Emulab pxeboot" is currently identified by
	 * its not being the default pxeboot and its path containing the
	 * string "pxeboot" anywhere.
257 258 259 260 261 262 263
	 */
	if (DEFINED(PXE_BOOT_PATH) &&
	    strstr(row[PXE_BOOT_PATH], "pxeboot") == NULL) {
		info->type = BIBOOTWHAT_TYPE_REBOOT;
		goto done;
	}

264
	/*
265 266 267
	 * Check next_boot_osid. It overrides the others. It should be
	 * the case that partition and path/mfs are mutually exclusive.
	 * mfs might be set when path is set.  
268
	 */
269 270 271 272
	if (DEFINED(NEXT_BOOT_OSID)) {
		if (DEFINED(NEXT_BOOT_PATH)) {
			if (DEFINED(NEXT_BOOT_MFS) && TOINT(NEXT_BOOT_MFS) == 1){
				info->type = BIBOOTWHAT_TYPE_MFS;
273
				parse_mfs_path(row[NEXT_BOOT_PATH], info);
274 275 276 277 278 279 280
			}
			else {
				info->type = BIBOOTWHAT_TYPE_MB;
				parse_multiboot_path(row[NEXT_BOOT_PATH], info);
			}
		}
		else if (DEFINED(NEXT_BOOT_PARTITION)) {
281 282 283 284 285 286 287 288
			if (bootdisk_bios_id) {
				info->type = BIBOOTWHAT_TYPE_DISKPART;
				info->what.dp.disk = bootdisk_bios_id;
				info->what.dp.partition = TOINT(NEXT_BOOT_PARTITION);
			} else {
				info->type = BIBOOTWHAT_TYPE_PART;
				info->what.partition = TOINT(NEXT_BOOT_PARTITION);
			}
289 290
		}
		else {
291 292
			error("Invalid NEXT_BOOT entry for host %s\n",
			      node_id ? node_id : ipstr);
293 294 295
			rval = 1;
		}
		if (DEFINED(NEXT_BOOT_CMDLINE)) {
296 297 298 299
			/*
			 * XXX note that this will override any cmdline
			 * specified in the osid path.  Should append instead?
			 */
300 301 302 303
			strncpy(info->cmdline,
				row[NEXT_BOOT_CMDLINE], MAX_BOOT_CMDLINE-1);
		}
		goto done;
304
	}
305 306 307 308 309 310 311 312

	/*
	 * Check temp_boot_osid. It overrides def_boot but not next_boot.
	 */
	if (DEFINED(TEMP_BOOT_OSID)) {
		if (DEFINED(TEMP_BOOT_PATH)) {
			if (DEFINED(TEMP_BOOT_MFS) && TOINT(TEMP_BOOT_MFS) == 1){
				info->type = BIBOOTWHAT_TYPE_MFS;
313
				parse_mfs_path(row[TEMP_BOOT_PATH], info);
314 315 316 317 318 319 320
			}
			else {
				info->type = BIBOOTWHAT_TYPE_MB;
				parse_multiboot_path(row[TEMP_BOOT_PATH], info);
			}
		}
		else if (DEFINED(TEMP_BOOT_PARTITION)) {
321 322 323 324 325 326 327 328
			if (bootdisk_bios_id) {
				info->type = BIBOOTWHAT_TYPE_DISKPART;
				info->what.dp.disk = bootdisk_bios_id;
				info->what.dp.partition = TOINT(TEMP_BOOT_PARTITION);
			} else {
				info->type = BIBOOTWHAT_TYPE_PART;
				info->what.partition = TOINT(TEMP_BOOT_PARTITION);
			}
329 330
		}
		else {
331 332
			error("Invalid TEMP_BOOT entry for host %s\n",
			      node_id ? node_id : ipstr);
333 334 335
			rval = 1;
		}
		goto done;
336
	}
337

338
	/*
339
	 * Lastly, def_boot.
340
	 */
341 342 343 344
	if (DEFINED(DEF_BOOT_OSID)) {
		if (DEFINED(DEF_BOOT_PATH)) {
			if (DEFINED(DEF_BOOT_MFS) && TOINT(DEF_BOOT_MFS) == 1) {
				info->type = BIBOOTWHAT_TYPE_MFS;
345
				parse_mfs_path(row[DEF_BOOT_PATH], info);
346 347 348 349 350 351 352
			}
			else {
				info->type = BIBOOTWHAT_TYPE_MB;
				parse_multiboot_path(row[DEF_BOOT_PATH], info);
			}
		}
		else if (DEFINED(DEF_BOOT_PARTITION)) {
353 354 355 356 357 358 359 360
			if (bootdisk_bios_id) {
				info->type = BIBOOTWHAT_TYPE_DISKPART;
				info->what.dp.disk = bootdisk_bios_id;
				info->what.dp.partition = TOINT(DEF_BOOT_PARTITION);
			} else {
				info->type = BIBOOTWHAT_TYPE_PART;
				info->what.partition = TOINT(DEF_BOOT_PARTITION);
			}
361 362
		}
		else {
363 364
			error("Invalid DEF_BOOT entry for host %s\n",
			      node_id ? node_id : ipstr);
365 366 367
			rval = 1;
		}
		if (DEFINED(DEF_BOOT_CMDLINE)) {
368 369 370 371
			/*
			 * XXX note that this will override any cmdline
			 * specified in the osid path.  Should append instead?
			 */
372 373 374 375 376
			strncpy(info->cmdline,
				row[DEF_BOOT_CMDLINE], MAX_BOOT_CMDLINE-1);
		}
		goto done;
	}
377 378 379 380
	/*
	 * If we get here, there is no bootinfo to give the client.
	 * New PXE boot clients get PXEWAIT, but older ones get an error.
	 */
381
	error("No OSIDs set for host %s\n", node_id ? node_id : ipstr);
382 383 384 385 386
	if (version >= 1) {
		info->type = BIBOOTWHAT_TYPE_WAIT;
		goto done;
	}
	rval = 1;
387
 done:
388
	mysql_free_result(res);
389
	return rval;
390 391
}

392 393 394
int
close_bootinfo_db(void)
{
395
	dbclose();
396 397 398
	return 0;
}

399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428
/*
 * Split a multiboot path into the IP and Path.
 */
static int
parse_multiboot_path(char *path, boot_what_t *info)
{
	char		*p  = path;
	struct hostent	*he;

	info->type = BIBOOTWHAT_TYPE_MB;
	info->what.mb.tftp_ip.s_addr = 0;

	strsep(&p, ":");
	if (p) {
		he = gethostbyname(path);
		path = p;
	}
	else {
		he = gethostbyname("users.emulab.net");
	}
	if (he) {
		memcpy((char *)&info->what.mb.tftp_ip,
		       he->h_addr, sizeof(info->what.mb.tftp_ip));
	}

	strncpy(info->what.mb.filename, path, MAX_BOOT_PATH-1);

	return 0;
}

429 430 431 432 433
/*
 * Arrange to boot the special newnode kernel.
 */
static int
boot_newnode_mfs(struct in_addr ipaddr, int version, boot_what_t *info)
434
{
435 436 437 438
	int		nrows;
	MYSQL_RES	*res;
	MYSQL_ROW	row;

439 440
	error("%s: nonexistent IP, booting '%s,%s'\n",
	      inet_ntoa(ipaddr), NEWNODEPID, NEWNODEOSID);
441

442 443
#define MFS_PATH	0

444 445
	res = mydb_query("select path from os_info where pid='%s' and "
			 "osname='%s' and mfs=1", 1, NEWNODEPID, NEWNODEOSID);
446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462

	if (!res) {
		error("Query failed\n");
		/* XXX Wrong. Should fail so client can request again later */
		return 0;
	}
	nrows = mysql_num_rows(res);

	switch (nrows) {
	case 0:
		error("No DB entry for OSID %s\n", NEWNODEOSID);
		mysql_free_result(res);
		return 1;
	case 1:
		break;
	default:
		error("Too many DB entries for OSID %s\n", NEWNODEOSID);
463
		mysql_free_result(res);
464 465 466 467 468 469
		return 1;
	}
	row = mysql_fetch_row(res);

	if (row[MFS_PATH] != 0 && row[MFS_PATH][0] != '\0') {
		info->type = BIBOOTWHAT_TYPE_MFS;
470
		parse_mfs_path(row[MFS_PATH], info);
471 472 473
		mysql_free_result(res);
		return 0;
	}
474
	mysql_free_result(res);
475 476 477
	error("No path info for OSID %s\n", NEWNODEOSID);
	return 1;
#undef  MFS_PATH
478 479
}

480 481 482 483 484
void
parse_mfs_path(char *str, boot_what_t *info)
{
	struct hostent *he;
	struct in_addr hip;
485 486 487 488 489 490 491 492
	char *path, *args;

	/* treat anything after a space as the command line */
	args = strchr(str, ' ');
	if (args != NULL) {
		*args++ = '\0';
		strncpy(info->cmdline, args, MAX_BOOT_CMDLINE-1);
	}
493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525

	/* no hostname, just copy string as is */
	path = strchr(str, ':');
	if (path == NULL) {
		strncpy(info->what.mfs, str, sizeof(info->what.mfs));
		return;
	}
	*path = '\0';

	/* hostname is a valid IP addr, copy as is */
	if (inet_addr(str) != INADDR_NONE) {
		*path = ':';
		strncpy(info->what.mfs, str, sizeof(info->what.mfs));
		return;
	}

	/* not a valid hostname, whine and copy it as is */
	he = gethostbyname(str);
	if (he == NULL) {
		*path = ':';
		error("Invalid hostname in MFS path '%s', passing anyway\n",
		      str);
		strncpy(info->what.mfs, str, sizeof(info->what.mfs));
		return;
	}
	*path = ':';

	/* valid hostname, translate to IP and replace in string */
	memcpy((char *)&hip, he->h_addr, he->h_length);
	strcpy(info->what.mfs, inet_ntoa(hip));
	strncat(info->what.mfs, path,
		sizeof(info->what.mfs)-strlen(info->what.mfs));
}
526

527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576
/*
 * ElabinElab hack. Its really terrible!
 */
#ifdef ELABINELAB
int
elabinelab_hackcheck(struct sockaddr_in *target)
{
	int		nrows;
	MYSQL_RES	*res;
	MYSQL_ROW	row;

	res = mydb_query("select i2.IP from interfaces as i1 "
			 "left join interfaces as i2 on i2.node_id=i1.node_id "
			 "     and i2.role='outer_ctrl' "
			 "where i1.IP='%s'", 1, inet_ntoa(target->sin_addr));
	if (!res) {
		error("elabinelab_hackcheck failed for host %s\n",
		      inet_ntoa(target->sin_addr));
		/* XXX Wrong. Should fail so client can request again later */
		return 0;
	}

	nrows = mysql_num_rows(res);

	switch (nrows) {
	case 0:
		/* No hack interface */
		mysql_free_result(res);
		return 0;
	case 1:
		break;
	default:
		error("elabinelab_hackcheck: Too many DB entries %s\n",
		      inet_ntoa(target->sin_addr));
		mysql_free_result(res);
		return 1;
	}
	row = mysql_fetch_row(res);

	if (row[0] != 0 && row[0][0] != '\0') {
		inet_aton(row[0], &(target->sin_addr));
		return 0;
	}
	mysql_free_result(res);
	error("elabinelab_hackcheck: No IP address %s\n",
	      inet_ntoa(target->sin_addr));
	return 1;
}
#endif

577 578 579
#ifdef TEST
#include <stdarg.h>

Mike Hibler's avatar
Mike Hibler committed
580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596
static void
print_bootwhat(boot_what_t *bootinfo)
{
	switch (bootinfo->type) {
	case BIBOOTWHAT_TYPE_PART:
		printf("boot from partition %d\n",
		       bootinfo->what.partition);
		break;
	case BIBOOTWHAT_TYPE_SYSID:
		printf("boot from partition with sysid %d\n",
		       bootinfo->what.sysid);
		break;
	case BIBOOTWHAT_TYPE_MB:
		printf("boot multiboot image %s:%s\n",
		       inet_ntoa(bootinfo->what.mb.tftp_ip),
		       bootinfo->what.mb.filename);
		break;
597 598 599 600 601 602
	case BIBOOTWHAT_TYPE_WAIT:
		printf("No boot; waiting till allocated\n");
		break;
	case BIBOOTWHAT_TYPE_MFS:
		printf("boot from MFS %s\n", bootinfo->what.mfs);
		break;
Mike Hibler's avatar
Mike Hibler committed
603
	}
604 605 606
	if (bootinfo->cmdline[0])
		printf("Command line %s\n", bootinfo->cmdline);
		
Mike Hibler's avatar
Mike Hibler committed
607 608
}

609
int
610 611 612 613 614 615
main(int argc, char **argv)
{
	struct in_addr ipaddr;
	boot_info_t boot_info;
	boot_what_t *boot_whatp = (boot_what_t *)&boot_info.data;

616
	open_bootinfo_db();
617 618
	while (--argc > 0) {
		if (inet_aton(*++argv, &ipaddr))
619
			if (query_bootinfo_db(ipaddr, 1, boot_whatp) == 0) {
Mike Hibler's avatar
Mike Hibler committed
620 621 622
				printf("%s: ", *argv);
				print_bootwhat(boot_whatp);
			} else
623
				printf("%s: failed\n", *argv);
624 625 626
		else
			printf("bogus IP address `%s'\n", *argv);
	}
627
	close_bootinfo_db();
628 629 630 631
	exit(0);
}
#endif
#endif