bootinfo_mysql.c 8.54 KB
Newer Older
Leigh B. Stoller's avatar
Leigh B. Stoller committed
1 2
/*
 * EMULAB-COPYRIGHT
3
 * Copyright (c) 2000-2004 University of Utah and the Flux Group.
Leigh B. Stoller's avatar
Leigh B. 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

16 17
#include "log.h"
#include "tbdb.h"
18
#include "bootwhat.h"
19
#include "bootinfo.h"
20 21
#include <mysql/mysql.h>

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

25 26
#ifdef USE_MYSQL_DB

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

int
open_bootinfo_db(void)
{
33 34 35
	if (!dbinit())
		return 1;

36 37 38
	return 0;
}

39 40 41 42 43 44 45 46
/*
  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.
*/

47
int
48
query_bootinfo_db(struct in_addr ipaddr, int version, boot_what_t *info)
49
{
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
	int		nrows, rval = 0;
	MYSQL_RES	*res;
	MYSQL_ROW	row;
	char		ipstr[32];

	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
#define DEFINED(x)		(row[(x)] != NULL && row[(x)][0] != '\0')
#define TOINT(x)		(atoi(row[(x)]))

	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 "
			 " 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 "
			 "where i.IP='%s'", 15, inet_ntoa(ipaddr));

	if (!res) {
		error("Query failed for host %s\n", ipstr);
		/* XXX Wrong. Should fail so client can request again later */
		return 0;
108
	}
109
	nrows = mysql_num_rows(res);
110 111 112 113

	switch (nrows) {
	case 0:
		mysql_free_result(res);
114
		return boot_newnode_mfs(ipaddr, version, info);
115 116 117
	case 1:
		break;
	default:
118
		error("%d entries for host %s\n", nrows, ipstr);
119 120 121
		break;
	}
	row = mysql_fetch_row(res);
Mike Hibler's avatar
Mike Hibler committed
122 123

	/*
124 125
	 * Version >=1 supports wait if not allocated. Client will recontact
	 * us later.
Mike Hibler's avatar
Mike Hibler committed
126
	 */
127 128 129
	if (version >= 1 && row[PID] == (char *) NULL) {
		info->type = BIBOOTWHAT_TYPE_WAIT;
		goto done;
Mike Hibler's avatar
Mike Hibler committed
130 131
	}

132
	/*
133 134 135
	 * 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.  
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
	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;
				strcpy(info->what.mfs, row[NEXT_BOOT_PATH]);
			}
			else {
				info->type = BIBOOTWHAT_TYPE_MB;
				parse_multiboot_path(row[NEXT_BOOT_PATH], info);
			}
		}
		else if (DEFINED(NEXT_BOOT_PARTITION)) {
			info->type = BIBOOTWHAT_TYPE_PART;
			info->what.partition = TOINT(NEXT_BOOT_PARTITION);
		}
		else {
			error("Invalid NEXT_BOOT entry for host %s\n", ipstr);
			rval = 1;
		}
		if (DEFINED(NEXT_BOOT_CMDLINE)) {
			strncpy(info->cmdline,
				row[NEXT_BOOT_CMDLINE], MAX_BOOT_CMDLINE-1);
		}
		goto done;
161
	}
162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185

	/*
	 * 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;
				strcpy(info->what.mfs, row[TEMP_BOOT_PATH]);
			}
			else {
				info->type = BIBOOTWHAT_TYPE_MB;
				parse_multiboot_path(row[TEMP_BOOT_PATH], info);
			}
		}
		else if (DEFINED(TEMP_BOOT_PARTITION)) {
			info->type = BIBOOTWHAT_TYPE_PART;
			info->what.partition = TOINT(TEMP_BOOT_PARTITION);
		}
		else {
			error("Invalid TEMP_BOOT entry for host %s\n", ipstr);
			rval = 1;
		}
		goto done;
186
	}
187

188
	/*
189
	 * Lastly, def_boot.
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
	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;
				strcpy(info->what.mfs, row[DEF_BOOT_PATH]);
			}
			else {
				info->type = BIBOOTWHAT_TYPE_MB;
				parse_multiboot_path(row[DEF_BOOT_PATH], info);
			}
		}
		else if (DEFINED(DEF_BOOT_PARTITION)) {
			info->type = BIBOOTWHAT_TYPE_PART;
			info->what.partition = TOINT(DEF_BOOT_PARTITION);
		}
		else {
			error("Invalid DEF_BOOT entry for host %s\n", ipstr);
			rval = 1;
		}
		if (DEFINED(DEF_BOOT_CMDLINE)) {
			strncpy(info->cmdline,
				row[DEF_BOOT_CMDLINE], MAX_BOOT_CMDLINE-1);
		}
		goto done;
	}
216 217 218 219 220 221 222 223 224 225
	/*
	 * If we get here, there is no bootinfo to give the client.
	 * New PXE boot clients get PXEWAIT, but older ones get an error.
	 */
	error("No OSIDs set for host %s\n", ipstr);
	if (version >= 1) {
		info->type = BIBOOTWHAT_TYPE_WAIT;
		goto done;
	}
	rval = 1;
226
 done:
227
	mysql_free_result(res);
228
	return rval;
229 230
}

231 232 233
int
close_bootinfo_db(void)
{
234
	dbclose();
235 236 237
	return 0;
}

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
/*
 * 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;
}

268 269 270 271 272
/*
 * Arrange to boot the special newnode kernel.
 */
static int
boot_newnode_mfs(struct in_addr ipaddr, int version, boot_what_t *info)
273
{
274 275 276 277 278 279
	int		nrows;
	MYSQL_RES	*res;
	MYSQL_ROW	row;

	error("%s: nonexistent IP, booting '%s'\n",
	      inet_ntoa(ipaddr), NEWNODEOSID);
280

281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314
#define MFS_PATH	0

	res = mydb_query("select path from os_info "
			 "where osid='%s' and mfs=1", 1, NEWNODEOSID);

	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);
		return 1;
	}
	row = mysql_fetch_row(res);

	if (row[MFS_PATH] != 0 && row[MFS_PATH][0] != '\0') {
		info->type = BIBOOTWHAT_TYPE_MFS;
		strcpy(info->what.mfs, row[MFS_PATH]);
		mysql_free_result(res);
		return 0;
	}
	error("No path info for OSID %s\n", NEWNODEOSID);
	return 1;
#undef  MFS_PATH
315 316
}

317 318 319 320

#ifdef TEST
#include <stdarg.h>

Mike Hibler's avatar
Mike Hibler committed
321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337
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;
338 339 340 341 342 343
	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
344
	}
345 346 347
	if (bootinfo->cmdline[0])
		printf("Command line %s\n", bootinfo->cmdline);
		
Mike Hibler's avatar
Mike Hibler committed
348 349
}

350
int
351 352 353 354 355 356
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;

357
	open_bootinfo_db();
358 359
	while (--argc > 0) {
		if (inet_aton(*++argv, &ipaddr))
360
			if (query_bootinfo_db(ipaddr, 1, boot_whatp) == 0) {
Mike Hibler's avatar
Mike Hibler committed
361 362 363
				printf("%s: ", *argv);
				print_bootwhat(boot_whatp);
			} else
364
				printf("%s: failed\n", *argv);
365 366 367
		else
			printf("bogus IP address `%s'\n", *argv);
	}
368
	close_bootinfo_db();
369 370 371 372
	exit(0);
}
#endif
#endif