bootinfosend.c 6.1 KB
Newer Older
1
/*
2
 * Copyright (c) 2000-2014 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/>.
 * 
 * }}}
22 23 24 25 26 27 28 29 30 31 32 33
 */

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <netdb.h>
34
#include "config.h"
35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
#include "log.h"
#include "tbdefs.h"
#include "bootwhat.h"
#include "bootinfo.h"

static void	log_bootwhat(struct in_addr ipaddr, boot_what_t *bootinfo);
int		debug = 0;
static char	*progname;

void
usage()
{
	fprintf(stderr,
		"Usage: %s <options> [-d] [-r | -q] target\n"
		"options:\n"
		"-d         - Turn on debugging\n"
Mike Hibler's avatar
Mike Hibler committed
51
		"-q         - Tell node to query bootinfo again\n"
52 53
		"-r         - Tell node to reboot\n"
		"-R         - Tell node to restart (re-DHCP to change server)\n",
54 55 56 57 58 59 60
		progname);
	exit(-1);
}

int
main(int argc, char **argv)
{
61 62
	int			sock, err, c;
	int			reboot = 0, restart = 0, query = 0;
63 64 65 66 67 68 69 70
	struct sockaddr_in	name, target;
	boot_info_t		boot_info;
	boot_what_t	       *boot_whatp = (boot_what_t *) &boot_info.data;
	extern char		build_info[];
	struct hostent	       *he;

	progname = argv[0];

71
	while ((c = getopt(argc, argv, "dhvrRq")) != -1) {
72 73 74 75 76 77 78
		switch (c) {
		case 'd':
			debug++;
			break;
		case 'r':
			reboot++;
			break;
79 80 81
		case 'R':
			restart++;
			break;
82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
		case 'q':
			query++;
			break;
		case 'v':
		    	fprintf(stderr, "%s\n", build_info);
			exit(0);
			break;
		case 'h':
		case '?':
		default:
			usage();
		}
	}
	argc -= optind;
	argv += optind;

	if (!argc)
		usage();
100
	if (query && (reboot || restart))
101 102 103 104 105 106
		usage();

	if (debug) 
		loginit(0, 0);
	else
		loginit(1, "bootinfo");
107 108 109

	if (debug)
		info("%s\n", build_info);
110

111
	/* Make sure we can map target. */
112 113 114 115 116 117 118 119 120 121 122 123
	if ((he = gethostbyname(argv[0])) == NULL) {
		errorc("gethostbyname(%s)", argv[0]);
		exit(1);
	}

	bzero(&target, sizeof(target));
	memcpy((char *)&target.sin_addr, he->h_addr, he->h_length);
	target.sin_family = AF_INET;
	target.sin_port   = htons((u_short) BOOTWHAT_SRCPORT);

	err = open_bootinfo_db();
	if (err) {
124
		error("could not open database\n");
125 126 127 128 129
		exit(1);
	}
#ifdef EVENTSYS
	err = bievent_init();
	if (err) {
130
		error("could not initialize event system\n");
131 132 133 134 135 136 137 138 139 140
		exit(1);
	}
#endif
	/* Create socket */
	sock = socket(AF_INET, SOCK_DGRAM, 0);
	if (sock < 0) {
		errorc("opening datagram socket");
		exit(1);
	}
	err = 1;
141
	if (setsockopt(sock, SOL_SOCKET, SO_REUSEPORT,
142 143 144 145 146 147 148 149 150 151 152 153 154 155
		       (char *)&err, sizeof(err)) < 0)
		errorc("setsockopt(SO_REUSEADDR)");
	
	/* Create name. */
	name.sin_family = AF_INET;
	name.sin_addr.s_addr = INADDR_ANY;
	name.sin_port = htons((u_short) BOOTWHAT_SENDPORT);
	if (bind(sock, (struct sockaddr *) &name, sizeof(name))) {
		errorc("binding datagram socket");
		exit(1);
	}

	bzero(&boot_info, sizeof(boot_info));
	boot_info.version = BIVERSION_CURRENT;
156 157 158
	if (reboot || restart) {
		boot_whatp->type = reboot ?
			BIBOOTWHAT_TYPE_REBOOT : BIBOOTWHAT_TYPE_RESTART;
159
#ifdef	EVENTSYS
160 161
		bievent_send(target.sin_addr, (void *) NULL,
			     TBDB_NODESTATE_SHUTDOWN);
162 163 164 165 166
#endif
	}
	else if (query) {
		boot_whatp->type = BIBOOTWHAT_TYPE_AUTO;
#ifdef	EVENTSYS
167 168
		bievent_send(target.sin_addr, (void *) NULL,
			     TBDB_NODESTATE_PXEWAKEUP);
169 170 171
#endif
	}
	else {
172
		err = query_bootinfo_db(target.sin_addr, NULL,
173
					boot_info.version,
174
					boot_whatp, NULL);
175 176 177 178
		if (err) {
			fatal("Could not send bootinfo packet!");
		}
#ifdef	EVENTSYS
179 180 181 182 183
		/*
		 * Keep the great state machine rolling forward...
		 * Pretend that the client sent a request before we
		 * send a reply.
		 */
184 185
		bievent_send(target.sin_addr, (void *) NULL,
			     TBDB_NODESTATE_PXEBOOTING);
186 187 188 189 190
		switch (boot_whatp->type) {
		case BIBOOTWHAT_TYPE_PART:
		case BIBOOTWHAT_TYPE_SYSID:
		case BIBOOTWHAT_TYPE_MB:
		case BIBOOTWHAT_TYPE_MFS:
191 192
			bievent_send(target.sin_addr, (void *) NULL,
				     TBDB_NODESTATE_BOOTING);
193 194 195
			break;
				
		case BIBOOTWHAT_TYPE_WAIT:
196 197
			bievent_send(target.sin_addr, (void *) NULL,
				     TBDB_NODESTATE_PXEWAIT);
198 199 200 201 202 203 204 205 206 207 208 209
			break;
		default:
			error("%s: invalid boot directive: %d\n",
			      inet_ntoa(target.sin_addr), boot_whatp->type);
			break;
		}
#endif
	}

	log_bootwhat(target.sin_addr, boot_whatp);
	boot_info.status  = BISTAT_SUCCESS;
	boot_info.opcode  = BIOPCODE_BOOTWHAT_ORDER;
210 211 212 213 214

#ifdef  ELABINELAB
	/* This is too brutal to even describe */
	elabinelab_hackcheck(&target);
#endif
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
	if (sendto(sock, (char *)&boot_info, sizeof(boot_info), 0,
		   (struct sockaddr *)&target, sizeof(target)) < 0)
		errorc("sendto");

	close(sock);
	close_bootinfo_db();
#ifdef  EVENTSYS
	bievent_shutdown();
#endif
	exit(0);
}

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:
		info("%s: SEND: boot from partition %d\n",
		     ipstr, bootinfo->what.partition);
		break;
	case BIBOOTWHAT_TYPE_SYSID:
		info("%s: SEND: boot from partition with sysid %d\n",
		     ipstr, bootinfo->what.sysid);
		break;
	case BIBOOTWHAT_TYPE_MB:
		info("%s: SEND: boot multiboot image %s:%s\n",
		       ipstr, inet_ntoa(bootinfo->what.mb.tftp_ip),
		       bootinfo->what.mb.filename);
		break;
	case BIBOOTWHAT_TYPE_WAIT:
		info("%s: SEND: wait mode\n", ipstr);
		break;
	case BIBOOTWHAT_TYPE_REBOOT:
		info("%s: SEND: reboot\n", ipstr);
		break;
	case BIBOOTWHAT_TYPE_AUTO:
		info("%s: SEND: query bootinfo\n", ipstr);
		break;
	case BIBOOTWHAT_TYPE_MFS:
		info("%s: SEND: boot from mfs %s\n", ipstr, bootinfo->what.mfs);
		break;
	}
}