Commit e416ceb4 authored by Leigh Stoller's avatar Leigh Stoller

HomeNet changes from Pat Gunn at CMU.

parent b09c42d0
......@@ -66,6 +66,13 @@ bootinfo(struct in_addr ipaddr, struct boot_info *boot_info, void *opaque)
boot_what_t *boot_whatp = (boot_what_t *) &boot_info->data;
switch (boot_info->opcode) {
case BIOPCODE_BOOTWHAT_KEYED_REQUEST:
info("%s: KEYED REQUEST (key=[%s], vers %d)\n",
inet_ntoa(ipaddr), boot_info->data, boot_info->version);
/* XXX For right now I'm not going to worry about making the event system work in this
code path. */
err = query_bootinfo_db(ipaddr, boot_info->version, boot_whatp, boot_info->data);
break;
case BIOPCODE_BOOTWHAT_REQUEST:
case BIOPCODE_BOOTWHAT_INFO:
info("%s: REQUEST (vers %d)\n",
......@@ -77,7 +84,7 @@ bootinfo(struct in_addr ipaddr, struct boot_info *boot_info, void *opaque)
TBDB_NODESTATE_PXEBOOTING);
#endif
err = query_bootinfo_db(ipaddr,
boot_info->version, boot_whatp);
boot_info->version, boot_whatp, NULL);
break;
default:
......
......@@ -12,7 +12,7 @@ int bootinfo_init(void);
int bootinfo(struct in_addr ipaddr,
struct boot_info *info, void *opaque);
int query_bootinfo_db(struct in_addr ipaddr, int version,
struct boot_what *info);
struct boot_what *info, char *key);
int elabinelab_hackcheck(struct sockaddr_in *target);
extern int debug;
......
......@@ -54,7 +54,7 @@ open_bootinfo_db(void)
}
int
query_bootinfo_db(struct in_addr ipaddr, int version, boot_what_t *info)
query_bootinfo_db(struct in_addr ipaddr, int version, boot_what_t *info, char *key)
{
struct config *configp;
......
......@@ -47,13 +47,20 @@ open_bootinfo_db(void)
*/
int
query_bootinfo_db(struct in_addr ipaddr, int version, boot_what_t *info)
query_bootinfo_db(struct in_addr ipaddr, int version, boot_what_t *info, char* key)
{
int nrows, rval = 0;
MYSQL_RES *res;
MYSQL_ROW row;
char ipstr[32];
int haskey=0;
char savedkey[HOSTKEY_LENGTH];
if(key != NULL)
{
strncpy(savedkey, key, HOSTKEY_LENGTH);
haskey=1;
}
info->cmdline[0] = 0; /* Must zero first byte! */
info->flags = 0;
strcpy(ipstr, inet_ntoa(ipaddr));
......@@ -77,40 +84,66 @@ query_bootinfo_db(struct in_addr ipaddr, int version, boot_what_t *info)
#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,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));
if(! haskey) {
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));
}
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);
}
if (!res) {
error("Query failed for host %s\n", ipstr);
/* XXX Wrong. Should fail so client can request again later */
......
......@@ -25,7 +25,7 @@ open_bootinfo_db(void)
}
int
query_bootinfo_db(struct in_addr ipaddr, int version, boot_what_t *info)
query_bootinfo_db(struct in_addr ipaddr, int version, boot_what_t *info, char* key)
{
#if 0
info->type = BIBOOTWHAT_TYPE_MB;
......
......@@ -27,9 +27,10 @@ void
usage()
{
fprintf(stderr,
"Usage: %s <options> [-d] [-s bossnode]\n"
"Usage: %s <options> [-d] [-k hostkey] [-s bossnode]\n"
"options:\n"
"-d - Turn on debugging\n"
"-k - Specify a widearea hostkey to be used instead of IP for identification\n"
"-s - Boss Node\n",
progname);
exit(-1);
......@@ -46,10 +47,11 @@ main(int argc, char **argv)
extern char build_info[];
struct hostent *he;
struct timeval timeout;
char* hostkey = NULL;
progname = argv[0];
while ((c = getopt(argc, argv, "dhvs:")) != -1) {
while ((c = getopt(argc, argv, "dhvk:s:")) != -1) {
switch (c) {
case 'd':
debug++;
......@@ -57,6 +59,9 @@ main(int argc, char **argv)
case 's':
bossnode = optarg;
break;
case 'k':
hostkey = optarg;
break;
case 'v':
fprintf(stderr, "%s\n", build_info);
exit(0);
......@@ -76,6 +81,12 @@ main(int argc, char **argv)
if (debug)
printf("%s\n", build_info);
if((hostkey != NULL) && ((strlen(hostkey) > HOSTKEY_LENGTH) || (strlen(hostkey) == 0))) {
warn("provided hostkey [%s] is not valid (too big or small)", hostkey);
exit(1);
}
/* Make sure we can map target */
if ((he = gethostbyname(bossnode)) == NULL) {
warn("gethostbyname(%s)", bossnode);
......@@ -121,6 +132,10 @@ main(int argc, char **argv)
bzero(&boot_info, sizeof(boot_info));
boot_info.version = BIVERSION_CURRENT;
boot_info.opcode = BIOPCODE_BOOTWHAT_REQUEST;
if(hostkey != NULL) {
strcpy(boot_info.data, hostkey);
boot_info.opcode = BIOPCODE_BOOTWHAT_KEYED_REQUEST;
}
while (1) {
struct sockaddr_in client;
......
......@@ -148,7 +148,7 @@ main(int argc, char **argv)
else {
err = query_bootinfo_db(target.sin_addr,
boot_info.version,
boot_whatp);
boot_whatp, NULL);
if (err) {
fatal("Could not send bootinfo packet!");
}
......
......@@ -26,6 +26,8 @@
#define MAX_BOOT_PATH 256
#define MAX_BOOT_CMDLINE ((MAX_BOOT_DATA - MAX_BOOT_PATH) - 32)
#define HOSTKEY_LENGTH 129
typedef struct boot_info {
short version;
short opcode;
......@@ -39,6 +41,7 @@ typedef struct boot_info {
#define BIOPCODE_BOOTWHAT_ACK 3 /* Ack to Reply */
#define BIOPCODE_BOOTWHAT_ORDER 4 /* Unsolicited command */
#define BIOPCODE_BOOTWHAT_INFO 5 /* Request for bootinfo */
#define BIOPCODE_BOOTWHAT_KEYED_REQUEST 6 /* Request for bootinfo, with key data stuffed into the data portion */
/* Version */
#define BIVERSION_CURRENT 1 /* First version is zero */
......
......@@ -1426,7 +1426,7 @@ CREATE TABLE `images` (
`ezid` tinyint(4) NOT NULL default '0',
`shared` tinyint(4) NOT NULL default '0',
`global` tinyint(4) NOT NULL default '0',
`mbr_version` tinyint(4) NOT NULL default '1',
`mbr_version` varchar(50) NOT NULL default '1',
`updated` datetime default NULL,
`access_key` varchar(64) default NULL,
PRIMARY KEY (`imageid`),
......@@ -4156,3 +4156,18 @@ CREATE TABLE `wires` (
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
---
--- Table structure for table `openvpn_config`
---
DROP TABLE IF EXISTS `sw_configfiles`;
CREATE TABLE `sw_configfiles` (
`id` int(11) NOT NULL auto_increment,
`node_id` varchar(32) NOT NULL,
`connection_id` int(11) NOT NULL default '0',
`file` varchar(4) NOT NULL,
`data` text,
`swid` varchar(20) NOT NULL,
PRIMARY KEY(`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
#
# HomeNet changes provided by Pat Gunn.
#
use strict;
use libdb;
sub DoUpdate($$$)
{
my ($dbhandle, $dbname, $version) = @_;
if (!DBTableExists("sw_configfiles")) {
DBQueryFatal("CREATE TABLE `sw_configfiles` ( ".
" `id` int(11) NOT NULL auto_increment, ".
" `node_id` varchar(32) NOT NULL, ".
" `connection_id` int(11) NOT NULL default '0', ".
" `file` varchar(4) NOT NULL, ".
" `data` text, ".
" `swid` varchar(20) NOT NULL, ".
" PRIMARY KEY(`id`) ".
") ENGINE=MyISAM DEFAULT CHARSET=latin1");
}
DBQueryFatal("ALTER TABLE images MODIFY mbr_version VARCHAR(50) ".
"NOT NULL default '1'");
return 0;
}
1;
......@@ -6036,7 +6036,7 @@ mydb_update(tmcdreq_t *reqp, char *query, ...)
* Map IP to node ID (plus other info).
*/
int
iptonodeid(tmcdreq_t *reqp, struct in_addr ipaddr)
iptonodeid(tmcdreq_t *reqp, struct in_addr ipaddr, char* nodekey)
{
MYSQL_RES *res;
MYSQL_ROW row;
......@@ -6052,7 +6052,30 @@ iptonodeid(tmcdreq_t *reqp, struct in_addr ipaddr)
* on the virtnodes. This is okay since all the routines that
* check jailflag also check to see if its a vnode or physnode.
*/
if (reqp->isvnode) {
if((key != NULL) && (strlen(nodekey) > 1) ) {
res = mydb_query(reqp, "select t.class,t.type,n.node_id,n.jailflag,"
" r.pid,r.eid,r.vname,e.gid,e.testdb, "
" n.update_accounts,n.role, "
" e.expt_head_uid,e.expt_swap_uid, "
" e.sync_server,t.class,t.type, "
" t.isremotenode,t.issubnode,e.keyhash, "
" nk.sfshostid,e.eventkey,0, "
" 0,e.elab_in_elab,e.elabinelab_singlenet, "
" e.idx "
"from nodes as n "
"left join reserved as r on "
" r.node_id=n.node_id "
"left join experiments as e on "
" e.pid=r.pid and e.eid=r.eid "
"left join node_types as t on "
" t.type=n.type "
"left join node_hostkeys as nk on "
" nk.node_id=n.node_id "
"where n.node_id in "
"(SELECT node_id FROM widearea_nodeinfo WHERE privkey='%s') "
26, nodekey);
}
else if (reqp->isvnode) {
res = mydb_query(reqp, "select vt.class,vt.type,np.node_id,"
" nv.jailflag,r.pid,r.eid,r.vname, "
" e.gid,e.testdb,nv.update_accounts, "
......@@ -6133,6 +6156,12 @@ iptonodeid(tmcdreq_t *reqp, struct in_addr ipaddr)
strncpy(reqp->pclass, row[14], sizeof(reqp->pclass));
strncpy(reqp->ptype, row[15], sizeof(reqp->ptype));
strncpy(reqp->nodeid, row[2], sizeof(reqp->nodeid));
if(nodekey != NULL) {
strncpy(reqp->privkey, nodekey, TBDB_FLEN_PRIVKEY);
}
else {
strcpy(reqp->privkey, "");
}
reqp->islocal = (! strcasecmp(row[16], "0") ? 1 : 0);
reqp->jailflag = (! strcasecmp(row[3], "0") ? 0 : 1);
reqp->issubnode = (! strcasecmp(row[17], "0") ? 0 : 1);
......
......@@ -63,6 +63,7 @@ typedef struct {
char eventkey[TBDB_FLEN_PRIVKEY];
char sfshostid[TBDB_FLEN_SFSHOSTID];
char testdb[TBDB_FLEN_TINYTEXT];
char privkey[TBDB_FLEN_PRIVKEY+1];
} tmcdreq_t;
typedef struct {
......
......@@ -788,16 +788,24 @@ handle_request(int sock, struct sockaddr_in *client, char *rdata, int istcp)
/*
* Map the ip to a nodeid.
*/
if ((err = iptonodeid(reqp, client->sin_addr))) {
if (reqp->isvnode) {
error("No such vnode %s associated with %s\n",
reqp->vnodeid, inet_ntoa(client->sin_addr));
if(havekey) {
if(err = iptonodeid(reqp, client->sin_addr, privkey)) {
error("No such node with wanode_key [%s]\n", privkey);
goto skipit;
}
else {
error("No such node: %s\n",
inet_ntoa(client->sin_addr));
}
else {
if ((err = iptonodeid(reqp, client->sin_addr, NULL))) {
if (reqp->isvnode) {
error("No such vnode %s associated with %s\n",
reqp->vnodeid, inet_ntoa(client->sin_addr));
}
else {
error("No such node: %s\n",
inet_ntoa(client->sin_addr));
}
goto skipit;
}
goto skipit;
}
/*
......
......@@ -232,6 +232,12 @@ main(int argc, char **argv)
usage();
}
#ifndef _WIN32
if( (! unixpath) && (0==access("/etc/emulab/emulab-privkey", R_OK)) ) {
keyfile = strdup("/etc/emulab/emulab-privkey");
}
#endif
#ifdef _WIN32
/*Windows requires us to start up the version of the network API that we want*/
if(WSAStartup( MAKEWORD( 2, 2 ), &wsaData )) {
......
......@@ -189,8 +189,9 @@ typedef struct {
char sfshostid[TBDB_FLEN_SFSHOSTID];
char testdb[TBDB_FLEN_TINYTEXT];
char tmcd_redirect[TBDB_FLEN_TINYTEXT];
char privkey[TBDB_FLEN_PRIVKEY+1];
} tmcdreq_t;
static int iptonodeid(struct in_addr, tmcdreq_t *);
static int iptonodeid(struct in_addr, tmcdreq_t *, char*);
static int checkdbredirect(tmcdreq_t *);
#ifdef EVENTSYS
......@@ -1025,18 +1026,25 @@ handle_request(int sock, struct sockaddr_in *client, char *rdata, int istcp)
/*
* Map the ip to a nodeid.
*/
if ((err = iptonodeid(client->sin_addr, reqp))) {
if (reqp->isvnode) {
error("No such vnode %s associated with %s\n",
reqp->vnodeid, inet_ntoa(client->sin_addr));
}
else {
error("No such node: %s\n",
inet_ntoa(client->sin_addr));
}
if(havekey) {
if((err = iptonodeid(client->sin_addr, reqp, privkey))) {
error("No such node with wanode_key [%s]\n", privkey);
goto skipit;
}
}
else {
if ((err = iptonodeid(client->sin_addr, reqp, NULL))) {
if (reqp->isvnode) {
error("No such vnode %s associated with %s\n",
reqp->vnodeid, inet_ntoa(client->sin_addr));
}
else {
error("No such node: %s\n",
inet_ntoa(client->sin_addr));
}
goto skipit;
}
}
/*
* Redirect geni sliver nodes to the tmcd of their origin.
*/
......@@ -3748,7 +3756,8 @@ COMMAND_PROTOTYPE(doloadinfo)
char buf[MYBUFSIZE];
char *bufp = buf, *ebufp = &buf[sizeof(buf)];
char *disktype, *useacpi, *useasf, address[MYBUFSIZE];
int disknum, zfill, mbrvers;
char mbrvers[51];
int disknum, zfill;
/*
* Get the address the node should contact to load its image
......@@ -3819,9 +3828,9 @@ COMMAND_PROTOTYPE(doloadinfo)
zfill = 0;
if (row[4] && row[4][0])
zfill = atoi(row[4]);
mbrvers = 1;
strcpy(mbrvers,"1");
if (row[5] && row[5][0])
mbrvers = atoi(row[5]);
strcpy(mbrvers, row[5]);
/*
* Get disk type and number
......@@ -3870,7 +3879,7 @@ COMMAND_PROTOTYPE(doloadinfo)
}
}
bufp += OUTPUT(bufp, ebufp - bufp,
" DISK=%s%d ZFILL=%d ACPI=%s MBRVERS=%d ASF=%s\n",
" DISK=%s%d ZFILL=%d ACPI=%s MBRVERS=%s ASF=%s\n",
disktype, disknum, zfill, useacpi, mbrvers, useasf);
mysql_free_result(res);
......@@ -4362,7 +4371,7 @@ mydb_update(char *query, ...)
* Map IP to node ID (plus other info).
*/
static int
iptonodeid(struct in_addr ipaddr, tmcdreq_t *reqp)
iptonodeid(struct in_addr ipaddr, tmcdreq_t *reqp, char* nodekey)
{
MYSQL_RES *res;
MYSQL_ROW row;
......@@ -4378,7 +4387,51 @@ iptonodeid(struct in_addr ipaddr, tmcdreq_t *reqp)
* on the virtnodes. This is okay since all the routines that
* check jailflag also check to see if its a vnode or physnode.
*/
if (reqp->isvnode) {
if((nodekey != NULL) && (strlen(nodekey) > 1) ) { /* Widearea nodes have wanodekeys that should be used to get the nodeid. */
res = mydb_query("SELECT t.class,t.type,n.node_id,n.jailflag,"
" r.pid,r.eid,r.vname,e.gid,e.testdb, "
" n.update_accounts,n.role, "
" e.expt_head_uid,e.expt_swap_uid, "
" e.sync_server,t.class,t.type, "
" t.isremotenode,t.issubnode,e.keyhash, "
" nk.sfshostid,e.eventkey,0, "
" 0,e.elab_in_elab,e.elabinelab_singlenet, "
" e.idx,e.creator_idx,e.swapper_idx, "
" u.admin,dedicated_wa_types.attrvalue "
" AS isdedicated_wa, "
" r.genisliver_idx,r.tmcd_redirect "
"FROM nodes AS n "
"LEFT JOIN reserved AS r ON "
" r.node_id=n.node_id "
"LEFT JOIN experiments AS e ON "
" e.pid=r.pid and e.eid=r.eid "
"LEFT JOIN node_types AS t ON "
" t.type=n.type "
"LEFT JOIN node_hostkeys AS nk ON "
" nk.node_id=n.node_id "
"LEFT JOIN users AS u ON "
" u.uid_idx=e.swapper_idx "
"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 "
"LEFT OUTER JOIN "
" (SELECT type,attrvalue "
" FROM node_type_attributes "
" WHERE attrkey='dedicated_widearea' "
" GROUP BY type) AS dedicated_wa_types "
" ON n.type=dedicated_wa_types.type "
"WHERE n.node_id IN "
"(SELECT node_id FROM widearea_nodeinfo WHERE privkey='%s') "
" AND nobootinfo_types.attrvalue IS NULL",
32, nodekey);
}
else if (reqp->isvnode) {
res = mydb_query("select vt.class,vt.type,np.node_id,"
" nv.jailflag,r.pid,r.eid,r.vname, "
" e.gid,e.testdb,nv.update_accounts, "
......@@ -4482,6 +4535,12 @@ iptonodeid(struct in_addr ipaddr, tmcdreq_t *reqp)
strncpy(reqp->pclass, row[14], sizeof(reqp->pclass));
strncpy(reqp->ptype, row[15], sizeof(reqp->ptype));
strncpy(reqp->nodeid, row[2], sizeof(reqp->nodeid));
if(nodekey != NULL) {
strncpy(reqp->privkey, nodekey, TBDB_FLEN_PRIVKEY);
}
else {
strcpy(reqp->privkey, "");
}
reqp->islocal = (! strcasecmp(row[16], "0") ? 1 : 0);
reqp->jailflag = (! strcasecmp(row[3], "0") ? 0 : 1);
reqp->issubnode = (! strcasecmp(row[17], "0") ? 0 : 1);
......@@ -7568,6 +7627,11 @@ COMMAND_PROTOTYPE(dobootwhat)
boot_info.opcode = BIOPCODE_BOOTWHAT_REQUEST;
boot_info.version = BIVERSION_CURRENT;
if(strlen(reqp->privkey) > 1) { /* We have a private key, so prepare bootinfo for it. */
boot_info.opcode = BIOPCODE_BOOTWHAT_KEYED_REQUEST;
strncpy(boot_info.data, reqp->privkey, TBDB_FLEN_PRIVKEY);
}
if (bootinfo(reqp->client, &boot_info, (void *) reqp)) {
OUTPUT(buf, sizeof(buf), "STATUS=failed\n");
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment