main.c 6.22 KB
Newer Older
1
/*
2
 * Copyright (c) 2000-2016 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
 */

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
29
#include <sys/param.h>
30 31 32 33 34 35 36 37 38 39 40 41 42
#include <paths.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <db.h>
#include <fcntl.h>
#include <time.h>
#include "log.h"
#include "tbdefs.h"
#include "bootwhat.h"
#include "bootinfo.h"

43 44 45 46 47 48 49
#ifdef LIBWRAP
#include <syslog.h>
#include <tcpd.h>
int allow_severity = LOG_TESTBED|LOG_INFO;
int deny_severity  = LOG_TESTBED|LOG_WARNING;
#endif

50 51 52 53 54 55 56
/*
 * 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

57
static void	log_bootwhat(struct in_addr, boot_what_t *, int);
58 59
static void	onhup(int sig);
static char	*progname;
60
static char     pidfile[MAXPATHLEN];
61
int		noevents = 0;
62 63 64 65 66 67 68 69
int		debug = 0;

void
usage()
{
	fprintf(stderr,
		"Usage: %s <options> [-d]\n"
		"options:\n"
70
		"-E         - Do not send BOOTING events\n"
71 72 73 74 75 76
		"-d         - Turn on debugging\n"
		"-p port    - Specify port number to listen on\n",
		progname);
	exit(-1);
}

77 78 79 80
static void
cleanup()
{
	unlink(pidfile);
81
	exit(0);
82 83
}

84 85 86
int
main(int argc, char **argv)
{
87
	int			sock, mlen, err, c;
88
	struct sockaddr_in	name, client;
89
	socklen_t		length;
90 91 92 93 94 95 96
	boot_info_t		boot_info;
	int		        port = BOOTWHAT_DSTPORT;
	FILE			*fp;
	extern char		build_info[];

	progname = argv[0];

97
	while ((c = getopt(argc, argv, "Ep:dhv")) != -1) {
98
		switch (c) {
99 100 101
		case 'E':
			noevents = 1;
			break;
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 129 130 131 132
		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;

	if (argc)
		usage();

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

133
	signal(SIGTERM, cleanup);
134 135 136
	/*
	 * Write out a pidfile.
	 */
137 138
	sprintf(pidfile, "%s/bootinfo.pid", _PATH_VARRUN);
	fp = fopen(pidfile, "w");
139 140 141 142 143 144 145
	if (fp != NULL) {
		fprintf(fp, "%d\n", getpid());
		(void) fclose(fp);
	}

	err = bootinfo_init();
	if (err) {
146
		error("could not initialize bootinfo\n");
147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173
		exit(1);
	}
	/* Create socket from which to read. */
	sock = socket(AF_INET, SOCK_DGRAM, 0);
	if (sock < 0) {
		errorc("opening datagram socket");
		exit(1);
	}
	
	/* Create name. */
	name.sin_family = AF_INET;
	name.sin_addr.s_addr = INADDR_ANY;
	name.sin_port = htons((u_short) port);
	if (bind(sock, (struct sockaddr *) &name, sizeof(name))) {
		errorc("binding datagram socket");
		exit(1);
	}
	/* Find assigned port value and print it out. */
	length = sizeof(name);
	if (getsockname(sock, (struct sockaddr *) &name, &length)) {
		errorc("getting socket name");
		exit(1);
	}
	info("listening on port %d\n", ntohs(name.sin_port));

	signal(SIGHUP, onhup);
	while (1) {
174
		int esent = 0;
175 176 177
#ifdef LIBWRAP
		struct request_info req;
#endif
178 179 180 181 182 183
		if ((mlen = recvfrom(sock, &boot_info, sizeof(boot_info),
				     0, (struct sockaddr *)&client, &length))
		    < 0) {
			errorc("receiving datagram packet");
			exit(1);
		}
184 185 186 187 188 189 190 191 192 193
#ifdef LIBWRAP
		request_init(&req, RQ_DAEMON, "bootinfo",
			     RQ_CLIENT_SIN, (struct sockaddr *)&client, 0);
		sock_methods(&req);
		if (!hosts_access(&req)) {
			info("%s: request denied by tcp wrappers\n",
			     inet_ntoa(client.sin_addr));
			continue;
		}
#endif
194
		err = bootinfo(client.sin_addr, (char *) NULL,
195
			       &boot_info, (void *) NULL, noevents, &esent);
196 197 198 199
		if (err < 0)
			continue;
		if (boot_info.status == BISTAT_SUCCESS)
			log_bootwhat(client.sin_addr,
200
				     (boot_what_t *) &boot_info.data, esent);
201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224

		boot_info.opcode = BIOPCODE_BOOTWHAT_REPLY;
		
		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)
			errorc("sendto");
	}
	close(sock);
	close_bootinfo_db();
	info("daemon terminating\n");
	exit(0);
}

static void
onhup(int sig)
{
	int err;

	info("re-initializing configuration database\n");
	close_bootinfo_db();
	err = open_bootinfo_db();
	if (err) {
225
		error("Could not reopen database\n");
226 227 228 229 230
		exit(1);
	}
}

static void
231
log_bootwhat(struct in_addr ipaddr, boot_what_t *bootinfo, int esent)
232
{
233
	char infostr[48];
234

235 236
	snprintf(infostr, sizeof(infostr), "%s: REPLY(%d): ",
		 inet_ntoa(ipaddr), esent);
237 238
	switch (bootinfo->type) {
	case BIBOOTWHAT_TYPE_PART:
239 240 241
		info("%sboot from partition %d\n",
		     infostr,
		     bootinfo->what.partition);
242
		break;
243
	case BIBOOTWHAT_TYPE_DISKPART:
244 245 246
		info("%sboot from disk/partition 0x%x/%d\n",
		     infostr,
		     bootinfo->what.dp.disk,
247 248
		     bootinfo->what.dp.partition);
		break;
249
	case BIBOOTWHAT_TYPE_SYSID:
250 251 252
		info("%sboot from partition with sysid %d\n",
		     infostr,
		     bootinfo->what.sysid);
253 254
		break;
	case BIBOOTWHAT_TYPE_MB:
255 256 257 258
		info("%sboot multiboot image %s:%s\n",
		     infostr,
		     inet_ntoa(bootinfo->what.mb.tftp_ip),
		     bootinfo->what.mb.filename);
259 260
		break;
	case BIBOOTWHAT_TYPE_WAIT:
261
		info("%swait mode\n", infostr);
262 263
		break;
	case BIBOOTWHAT_TYPE_MFS:
264
		info("%sboot from mfs %s\n", infostr, bootinfo->what.mfs);
265 266
		break;
	case BIBOOTWHAT_TYPE_REBOOT:
267
		info("%sreboot (alternate PXE boot)\n", infostr);
268
		break;
269
	default:
270
		info("%sUNKNOWN (type=%d)\n", infostr, bootinfo->type);
271
		break;
272 273
	}
	if (bootinfo->cmdline[0]) {
274
		info("%scommand line: %s\n", infostr, bootinfo->cmdline);
275 276 277
	}
}