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

Leigh Stoller's avatar
Leigh Stoller committed
7 8 9
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
10
#include <arpa/inet.h>
Leigh Stoller's avatar
Leigh Stoller committed
11
#include <stdio.h>
12
#include <paths.h>
13 14 15
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
16
#include <signal.h>
17 18
#include <db.h>
#include <fcntl.h>
19 20
#include "log.h"
#include "tbdefs.h"
21
#include "bootwhat.h"
22
#include "bootinfo.h"
Leigh Stoller's avatar
Leigh Stoller committed
23

24 25 26 27 28 29 30
/*
 * Minimum number of seconds that must pass before we send another
 * event for a node. This is to decrease the number of spurious events
 * we get from nodes when bootinfo packets are lost. 
 */
#define MINEVENTTIME	10

31 32 33
static void	log_bootwhat(struct in_addr ipaddr, boot_what_t *bootinfo);
static void	onhup(int sig);
static char	*progname;
34 35 36
static int	bicache_init(void);
static int	bicache_shutdown(void);
static int	bicache_needevent(struct in_addr ipaddr);
37
int		debug = 0;
Leigh Stoller's avatar
Leigh Stoller committed
38

39 40
void
usage()
Leigh Stoller's avatar
Leigh Stoller committed
41
{
42 43 44 45 46 47 48 49 50 51 52 53 54
	fprintf(stderr,
		"Usage: %s <options> [-d]\n"
		"options:\n"
		"-d         - Turn on debugging\n"
		"-p port    - Specify port number to listen on\n",
		progname);
	exit(-1);
}

int
main(int argc, char **argv)
{
	int			sock, length, mlen, err, c;
Leigh Stoller's avatar
Leigh Stoller committed
55 56 57
	struct sockaddr_in	name, client;
	boot_info_t		boot_info;
	boot_what_t	       *boot_whatp = (boot_what_t *) &boot_info.data;
58
	int		        port = BOOTWHAT_DSTPORT;
59 60
	char			buf[BUFSIZ];
	FILE			*fp;
61
	extern char		build_info[];
Leigh Stoller's avatar
Leigh Stoller committed
62

63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
	progname = argv[0];

	while ((c = getopt(argc, argv, "p:dhv")) != -1) {
		switch (c) {
		case 'd':
			debug++;
			break;
		case 'p':
			port = atoi(optarg);
			break;
		case 'v':
		    	fprintf(stderr, "%s\n", build_info);
			exit(0);
			break;
		case 'h':
		case '?':
		default:
			usage();
		}
	}
	argc -= optind;
	argv += optind;
85

86 87 88 89 90 91 92 93 94 95 96
	if (argc)
		usage();

	if (debug) 
		loginit(0, 0);
	else {
		/* Become a daemon */
		daemon(0, 0);
		loginit(1, "bootinfo");
	}
	info("%s\n", build_info);
97

98 99 100 101 102 103 104 105 106 107
	/*
	 * Write out a pidfile.
	 */
	sprintf(buf, "%s/bootinfo.pid", _PATH_VARRUN);
	fp = fopen(buf, "w");
	if (fp != NULL) {
		fprintf(fp, "%d\n", getpid());
		(void) fclose(fp);
	}

108 109 110
	/* Initialize data base */
	err = open_bootinfo_db();
	if (err) {
111 112 113
		error("could not open database");
		exit(1);
	}
114 115 116 117 118
	err = bicache_init();
	if (err) {
		error("could not initialize cache");
		exit(1);
	}
119 120 121 122
#ifdef EVENTSYS
	err = bievent_init();
	if (err) {
		error("could not initialize event system");
123 124
		exit(1);
	}
125
#endif
Leigh Stoller's avatar
Leigh Stoller committed
126 127 128
	/* Create socket from which to read. */
	sock = socket(AF_INET, SOCK_DGRAM, 0);
	if (sock < 0) {
129
		errorc("opening datagram socket");
Leigh Stoller's avatar
Leigh Stoller committed
130 131 132 133 134 135
		exit(1);
	}
	
	/* Create name. */
	name.sin_family = AF_INET;
	name.sin_addr.s_addr = INADDR_ANY;
136
	name.sin_port = htons((u_short) port);
Leigh Stoller's avatar
Leigh Stoller committed
137
	if (bind(sock, (struct sockaddr *) &name, sizeof(name))) {
138
		errorc("binding datagram socket");
Leigh Stoller's avatar
Leigh Stoller committed
139 140 141 142 143
		exit(1);
	}
	/* Find assigned port value and print it out. */
	length = sizeof(name);
	if (getsockname(sock, (struct sockaddr *) &name, &length)) {
144
		errorc("getting socket name");
Leigh Stoller's avatar
Leigh Stoller committed
145 146
		exit(1);
	}
147
	info("listening on port %d\n", ntohs(name.sin_port));
Leigh Stoller's avatar
Leigh Stoller committed
148

149
	signal(SIGHUP, onhup);
Leigh Stoller's avatar
Leigh Stoller committed
150
	while (1) {
151 152
		int	needevent = 1;
		
Leigh Stoller's avatar
Leigh Stoller committed
153 154 155
		if ((mlen = recvfrom(sock, &boot_info, sizeof(boot_info),
				     0, (struct sockaddr *)&client, &length))
		    < 0) {
156
			errorc("receiving datagram packet");
Leigh Stoller's avatar
Leigh Stoller committed
157 158 159
			exit(1);
		}

160 161
		switch (boot_info.opcode) {
		case BIOPCODE_BOOTWHAT_REQUEST:
162 163 164 165
		case BIOPCODE_BOOTWHAT_INFO:
			info("%s: REQUEST (vers %d)\n",
			     inet_ntoa(client.sin_addr), boot_info.version);
#ifdef	EVENTSYS
166 167 168 169
			needevent = bicache_needevent(client.sin_addr);
			if (needevent)
				bievent_send(client.sin_addr,
					     TBDB_NODESTATE_PXEBOOTING);
170 171 172 173
#endif
			err = query_bootinfo_db(client.sin_addr,
						boot_info.version,
						boot_whatp);
174 175 176
			break;

		default:
177 178
			info("%s: invalid packet %d\n",
			     inet_ntoa(client.sin_addr), boot_info.opcode);
Leigh Stoller's avatar
Leigh Stoller committed
179 180
			continue;
		}
181
		
182
		if (err)
183
			boot_info.status = BISTAT_FAIL;
184
		else {
185
			boot_info.status = BISTAT_SUCCESS;
186 187
			log_bootwhat(client.sin_addr, boot_whatp);
#ifdef	EVENTSYS
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207
			if (needevent) {
				switch (boot_whatp->type) {
				case BIBOOTWHAT_TYPE_PART:
				case BIBOOTWHAT_TYPE_SYSID:
				case BIBOOTWHAT_TYPE_MB:
				case BIBOOTWHAT_TYPE_MFS:
					bievent_send(client.sin_addr,
						     TBDB_NODESTATE_BOOTING);
					break;
					
				case BIBOOTWHAT_TYPE_WAIT:
					bievent_send(client.sin_addr,
						     TBDB_NODESTATE_PXEWAIT);
					break;
				default:
					error("%s: invalid boot directive: %d\n",
					      inet_ntoa(client.sin_addr),
					      boot_whatp->type);
					break;
				}
208 209
			}
#endif
210
		}
211 212
		boot_info.opcode = BIOPCODE_BOOTWHAT_REPLY;
		
Leigh Stoller's avatar
Leigh Stoller committed
213 214 215 216
		client.sin_family = AF_INET;
		client.sin_port = htons(BOOTWHAT_SRCPORT);
		if (sendto(sock, (char *)&boot_info, sizeof(boot_info), 0,
			(struct sockaddr *)&client, sizeof(client)) < 0)
217
			errorc("sendto");
Leigh Stoller's avatar
Leigh Stoller committed
218 219
	}
	close(sock);
220
	close_bootinfo_db();
221 222 223
#ifdef  EVENTSYS
	bievent_shutdown();
#endif
224
	bicache_shutdown();
225
	info("daemon terminating\n");
226
	exit(0);
Leigh Stoller's avatar
Leigh Stoller committed
227 228
}

229 230 231 232 233
static void
onhup(int sig)
{
	int err;

234
	info("re-initializing configuration database\n");
235 236 237
	close_bootinfo_db();
	err = open_bootinfo_db();
	if (err) {
238
		error("Could not reopen database");
239 240 241 242
		exit(1);
	}
}

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 275 276 277 278 279 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 315 316 317 318 319 320 321 322 323 324
/*
 * Simple cache to prevent dups when bootinfo packets get lost.
 */
static DB      *dbp;

/*
 * Initialize an in-memory DB
 */
static int
bicache_init(void)
{
	if ((dbp =
	     dbopen(NULL, O_CREAT|O_TRUNC|O_RDWR, 664, DB_HASH, NULL)) == NULL) {
		errorc("failed to initialize the bootinfo DBM");
		return 1;
	}
	return 0;
}

static int
bicache_shutdown(void)
{
	if (dbp) {
		if (dbp->close(dbp) < 0) {
			errorc("failed to sutdown the bootinfo DBM");
			return 1;
		}
	}
	return 0;
}

/*
 * This does both a check and an insert. The idea is that we store the
 * current time of the request, returning yes/no to the caller if the
 * current request is is within a small delta of the previous request.
 * This should keep the number of repeats to a minimum, since a requests
 * coming within a few seconds of each other indicate lost bootinfo packets.
 */
static int
bicache_needevent(struct in_addr ipaddr)
{
	DBT	key, item;
	time_t  tt = time(NULL);
	int	rval = 1, r;

	key.data = (void *) &ipaddr;
	key.size = sizeof(ipaddr);

	/*
	 * First find current value.
	 */
	if ((r = (dbp->get)(dbp, &key, &item, NULL)) != NULL) {
		if (r == -1) {
			errorc("Could not retrieve entry from DBM for %s\n",
			       inet_ntoa(ipaddr));
		}
	}
	if (r == NULL) {
		time_t	oldtt = *((time_t *)item.data);

		if (debug) {
			info("Timestamps: old:%ld new:%ld\n", oldtt, tt);
		}

		if (tt - oldtt <= MINEVENTTIME) {
			rval = 0;
			info("%s: no event will be sent: last:%ld cur:%ld\n",
			     inet_ntoa(ipaddr), oldtt, tt);
		}
	}
	if (rval) {
		item.data = (void *) &tt;
		item.size = sizeof(tt);

		if ((dbp->put)(dbp, &key, &item, NULL) != NULL) {
			errorc("Could not insert DBM entry for %s\n",
			       inet_ntoa(ipaddr));
		}
	}
	return rval;
}

325 326 327 328 329 330 331 332
static void
log_bootwhat(struct in_addr ipaddr, boot_what_t *bootinfo)
{
	char ipstr[32];

	strncpy(ipstr, inet_ntoa(ipaddr), sizeof ipstr);
	switch (bootinfo->type) {
	case BIBOOTWHAT_TYPE_PART:
333 334
		info("%s: REPLY: boot from partition %d\n",
		     ipstr, bootinfo->what.partition);
335 336
		break;
	case BIBOOTWHAT_TYPE_SYSID:
337 338
		info("%s: REPLY: boot from partition with sysid %d\n",
		     ipstr, bootinfo->what.sysid);
339 340
		break;
	case BIBOOTWHAT_TYPE_MB:
341 342
		info("%s: REPLY: boot multiboot image %s:%s\n",
		       ipstr, inet_ntoa(bootinfo->what.mb.tftp_ip),
343 344
		       bootinfo->what.mb.filename);
		break;
345 346 347 348 349 350
	case BIBOOTWHAT_TYPE_WAIT:
		info("%s: REPLY: wait mode\n", ipstr);
		break;
	case BIBOOTWHAT_TYPE_MFS:
		info("%s: REPLY: boot from mfs %s\n", ipstr, bootinfo->what.mfs);
		break;
351
	}
352 353 354
	if (bootinfo->cmdline[0]) {
		info("%s: REPLY: command line: %s\n", ipstr, bootinfo->cmdline);
	}
355
}
356