Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/stand/common/install.c
34677 views
1
/*-
2
* Copyright (c) 2008-2014, Juniper Networks, Inc.
3
* All rights reserved.
4
*
5
* Redistribution and use in source and binary forms, with or without
6
* modification, are permitted provided that the following conditions
7
* are met:
8
* 1. Redistributions of source code must retain the above copyright
9
* notice, this list of conditions and the following disclaimer.
10
* 2. Redistributions in binary form must reproduce the above copyright
11
* notice, this list of conditions and the following disclaimer in the
12
* documentation and/or other materials provided with the distribution.
13
*
14
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
19
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
21
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
22
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24
* SUCH DAMAGE.
25
*/
26
27
#include <sys/param.h>
28
#include <sys/socket.h>
29
#include <net/if.h>
30
#include <netinet/in.h>
31
#include <netinet/in_systm.h>
32
33
#include <stand.h>
34
#include <net.h>
35
#include <string.h>
36
37
#include "bootstrap.h"
38
39
extern struct in_addr servip;
40
41
extern int pkgfs_init(const char *, struct fs_ops *);
42
extern void pkgfs_cleanup(void);
43
44
COMMAND_SET(install, "install", "install software package", command_install);
45
46
static char *inst_kernel;
47
static char **inst_modules;
48
static char *inst_rootfs;
49
static char *inst_loader_rc;
50
51
static int
52
setpath(char **what, char *val)
53
{
54
char *path;
55
size_t len;
56
int rel;
57
58
len = strlen(val) + 1;
59
rel = (val[0] != '/') ? 1 : 0;
60
path = malloc(len + rel);
61
if (path == NULL)
62
return (ENOMEM);
63
path[0] = '/';
64
strcpy(path + rel, val);
65
66
*what = path;
67
return (0);
68
}
69
70
static int
71
setmultipath(char ***what, char *val)
72
{
73
char *s, *v;
74
int count, error, idx;
75
76
count = 0;
77
v = val;
78
do {
79
count++;
80
s = strchr(v, ',');
81
v = (s == NULL) ? NULL : s + 1;
82
} while (v != NULL);
83
84
*what = calloc(count + 1, sizeof(char *));
85
if (*what == NULL)
86
return (ENOMEM);
87
88
for (idx = 0; idx < count; idx++) {
89
s = strchr(val, ',');
90
if (s != NULL)
91
*s++ = '\0';
92
error = setpath(*what + idx, val);
93
if (error)
94
return (error);
95
val = s;
96
}
97
98
return (0);
99
}
100
101
static int
102
read_metatags(int fd)
103
{
104
char buf[1024];
105
char *p, *tag, *val;
106
ssize_t fsize;
107
int error;
108
109
fsize = read(fd, buf, sizeof(buf));
110
if (fsize == -1)
111
return (errno);
112
113
/*
114
* Assume that if we read a whole buffer worth of data, we
115
* haven't read the entire file. In other words, the buffer
116
* size must always be larger than the file size. That way
117
* we can append a '\0' and use standard string operations.
118
* Return an error if this is not possible.
119
*/
120
if (fsize == sizeof(buf))
121
return (ENOMEM);
122
123
buf[fsize] = '\0';
124
error = 0;
125
tag = buf;
126
while (!error && *tag != '\0') {
127
val = strchr(tag, '=');
128
if (val == NULL) {
129
error = EINVAL;
130
break;
131
}
132
*val++ = '\0';
133
p = strchr(val, '\n');
134
if (p == NULL) {
135
error = EINVAL;
136
break;
137
}
138
*p++ = '\0';
139
140
if (strncmp(tag, "ENV_", 4) == 0)
141
setenv(&tag[4], val, 1);
142
else if (strcmp(tag, "KERNEL") == 0)
143
error = setpath(&inst_kernel, val);
144
else if (strcmp(tag, "MODULES") == 0)
145
error = setmultipath(&inst_modules, val);
146
else if (strcmp(tag, "ROOTFS") == 0)
147
error = setpath(&inst_rootfs, val);
148
else if (strcmp(tag, "LOADER_RC") == 0)
149
error = setpath(&inst_loader_rc, val);
150
151
tag = p;
152
}
153
154
return (error);
155
}
156
157
static void
158
cleanup(void)
159
{
160
u_int i;
161
162
if (inst_kernel != NULL) {
163
free(inst_kernel);
164
inst_kernel = NULL;
165
}
166
if (inst_modules != NULL) {
167
i = 0;
168
while (inst_modules[i] != NULL)
169
free(inst_modules[i++]);
170
free(inst_modules);
171
inst_modules = NULL;
172
}
173
if (inst_rootfs != NULL) {
174
free(inst_rootfs);
175
inst_rootfs = NULL;
176
}
177
if (inst_loader_rc != NULL) {
178
free(inst_loader_rc);
179
inst_loader_rc = NULL;
180
}
181
pkgfs_cleanup();
182
}
183
184
/*
185
* usage: install URL
186
* where: URL = tftp://[host]/<package>
187
* or file://[devname[:fstype]]/<package>
188
*/
189
static int
190
install(char *pkgname)
191
{
192
static char buf[256];
193
struct fs_ops *proto;
194
struct preloaded_file *fp;
195
char *e, *s, *currdev;
196
char *devname;
197
size_t devnamelen;
198
int error, fd, i, local;
199
200
s = strstr(pkgname, "://");
201
if (s == NULL)
202
goto invalid_url;
203
204
i = s - pkgname;
205
s += 3;
206
if (*s == '\0')
207
goto invalid_url;
208
209
devname = NULL;
210
devnamelen = 0;
211
proto = NULL;
212
local = 0;
213
214
if (i == 4 && !strncasecmp(pkgname, "tftp", i)) {
215
devname = "net0";
216
devnamelen = 4;
217
netproto = NET_TFTP;
218
proto = &tftp_fsops;
219
} else if (i == 4 && !strncasecmp(pkgname, "file", i)) {
220
currdev = getenv("currdev");
221
local = 1;
222
223
if (*s == '/') { /* file:/// */
224
if (devname == NULL)
225
devname = currdev;
226
if (devname == NULL)
227
devname = "disk1";
228
} else { /* file://devname[:fstype]/ */
229
devname = s;
230
e = strchr(devname, '/');
231
if (!e)
232
goto invalid_url;
233
devnamelen = e - devname;
234
s = e; /* consume devname */
235
}
236
if ((e = strchr(devname, ':')) != NULL) {
237
/* could be :fstype */
238
devnamelen = e - devname;
239
switch (e[1]) {
240
case '\0': /* just currdev */
241
break;
242
case 'd':
243
proto = &dosfs_fsops;
244
break;
245
#ifdef HOSTPROG
246
case 'h':
247
{
248
extern struct fs_ops host_fsops;
249
250
proto = &host_fsops;
251
}
252
break;
253
#endif
254
case 'u':
255
proto = &ufs_fsops;
256
break;
257
}
258
}
259
if (proto == NULL && strncmp(devname, "disk", 4) == 0) {
260
proto = &dosfs_fsops;
261
}
262
}
263
264
if (devname == NULL)
265
goto invalid_url;
266
267
if (devnamelen == 0) {
268
/* default is currdev which ends with ':' */
269
devnamelen = strlen(devname);
270
if (devname[devnamelen - 1] == ':')
271
devnamelen--;
272
}
273
274
if (*s != '/' ) {
275
if (local)
276
goto invalid_url;
277
278
pkgname = strchr(s, '/');
279
if (pkgname == NULL)
280
goto invalid_url;
281
282
*pkgname = '\0';
283
servip.s_addr = inet_addr(s);
284
if (servip.s_addr == htonl(INADDR_NONE))
285
goto invalid_url;
286
287
setenv("serverip", inet_ntoa(servip), 1);
288
289
*pkgname = '/';
290
} else
291
pkgname = s;
292
293
i = snprintf(buf, sizeof(buf), "%.*s:%s",
294
(int) devnamelen, devname, pkgname);
295
if (i >= (int) sizeof(buf)) {
296
command_errmsg = "package name too long";
297
return (CMD_ERROR);
298
}
299
setenv("install_package", buf, 1);
300
301
error = pkgfs_init(buf, proto);
302
if (error) {
303
command_errmsg = "cannot open package";
304
goto fail;
305
}
306
307
/*
308
* Point of no return: unload anything that may have been
309
* loaded and prune the environment from harmful variables.
310
*/
311
unload();
312
unsetenv("vfs.root.mountfrom");
313
314
/*
315
* read the metatags file.
316
*/
317
fd = open("/metatags", O_RDONLY);
318
if (fd != -1) {
319
error = read_metatags(fd);
320
close(fd);
321
if (error) {
322
command_errmsg = "cannot load metatags";
323
goto fail;
324
}
325
}
326
327
s = (inst_kernel == NULL) ? "/kernel" : inst_kernel;
328
error = mod_loadkld(s, 0, NULL);
329
if (error) {
330
command_errmsg = "cannot load kernel from package";
331
goto fail;
332
}
333
334
/* If there is a loader.rc in the package, execute it */
335
s = (inst_loader_rc == NULL) ? "/loader.rc" : inst_loader_rc;
336
fd = open(s, O_RDONLY);
337
if (fd != -1) {
338
close(fd);
339
error = interp_include(s);
340
if (error == CMD_ERROR)
341
goto fail;
342
}
343
344
i = 0;
345
while (inst_modules != NULL && inst_modules[i] != NULL) {
346
error = mod_loadkld(inst_modules[i], 0, NULL);
347
if (error) {
348
command_errmsg = "cannot load module(s) from package";
349
goto fail;
350
}
351
i++;
352
}
353
354
s = (inst_rootfs == NULL) ? "/install.iso" : inst_rootfs;
355
if (file_loadraw(s, "mfs_root", 1) == NULL) {
356
error = errno;
357
command_errmsg = "cannot load root file system";
358
goto fail;
359
}
360
361
cleanup();
362
363
fp = file_findfile(NULL, NULL);
364
if (fp != NULL)
365
file_formats[fp->f_loader]->l_exec(fp);
366
error = CMD_ERROR;
367
command_errmsg = "unable to start installation";
368
369
fail:
370
sprintf(buf, "%s (error %d)", command_errmsg, error);
371
cleanup();
372
unload();
373
exclusive_file_system = NULL;
374
command_errmsg = buf; /* buf is static. */
375
return (CMD_ERROR);
376
377
invalid_url:
378
command_errmsg = "invalid URL";
379
return (CMD_ERROR);
380
}
381
382
static int
383
command_install(int argc, char *argv[])
384
{
385
int argidx;
386
387
unsetenv("install_format");
388
389
argidx = 1;
390
while (1) {
391
if (argc == argidx) {
392
command_errmsg =
393
"usage: install [--format] <URL>";
394
return (CMD_ERROR);
395
}
396
if (!strcmp(argv[argidx], "--format")) {
397
setenv("install_format", "yes", 1);
398
argidx++;
399
continue;
400
}
401
break;
402
}
403
404
return (install(argv[argidx]));
405
}
406
407