1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21/*
22 * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 */
25
26/*
27 * I18N message number ranges
28 *  This file: 6000 - 6499
29 *  Shared common messages: 1 - 1999
30 */
31
32
33
34#include <stdio.h>
35#include <unistd.h>
36#include <stdlib.h>
37#include <string.h>
38#include <fcntl.h>
39#include <errno.h>
40#include <sys/param.h>
41#include <sys/mnttab.h>
42#include <sys/types.h>
43#include <sys/stat.h>
44#include <sys/openpromio.h>
45
46
47/*
48 * For i18n
49 */
50#include <stgcom.h>
51
52
53/*
54 * 128 is the size of the largest (currently) property name
55 * 8192 - MAXPROPSIZE - sizeof (int) is the size of the largest
56 * (currently) property value, viz. nvramrc.
57 * the sizeof(uint_t) is from struct openpromio
58 */
59#define	MAXPROPSIZE		128
60#define	MAXVALSIZE		(8192 - MAXPROPSIZE - sizeof (uint_t))
61
62#define	BOOTDEV_PROP_NAME	"boot-device"
63
64static int getbootdevname(char *, char *);
65static int setprom(unsigned, unsigned, char *);
66extern int devfs_dev_to_prom_name(char *, char *);
67
68/*
69 * Call getbootdevname() to get the absolute pathname of boot device
70 * and call setprom() to set the boot-device variable.
71 */
72int
73setboot(unsigned int yes, unsigned int verbose, char *fname)
74{
75	char	bdev[MAXPATHLEN];
76
77	if (!getbootdevname(fname, bdev)) {
78		(void) fprintf(stderr, MSGSTR(6000,
79			"Cannot determine device name for %s\n"),
80			fname);
81		return (errno);
82	}
83
84	return (setprom(yes, verbose, bdev));
85}
86
87/*
88 * Read the mnttab and resolve the special device of the fs we are
89 * interested in, into an absolute pathname
90 */
91static int
92getbootdevname(char *bootfs, char *bdev)
93{
94	FILE *f;
95	char *fname;
96	char *devname;
97	struct mnttab m;
98	struct stat sbuf;
99	int mountpt = 0;
100	int found = 0;
101
102	devname = bootfs;
103
104	if (stat(bootfs, &sbuf) < 0) {
105		perror(MSGSTR(6001, "stat"));
106		return (0);
107	}
108
109	switch (sbuf.st_mode & S_IFMT) {
110		case S_IFBLK:
111			break;
112		default:
113			mountpt = 1;
114			break;
115	}
116
117	if (mountpt) {
118		fname = MNTTAB;
119		f = fopen(fname, "r");
120		if (f == NULL) {
121			perror(fname);
122			return (0);
123		}
124
125		while (getmntent(f, &m) == 0) {
126			if (strcmp(m.mnt_mountp, bootfs))
127				continue;
128			else {
129				found = 1;
130				break;
131			}
132		}
133
134		(void) fclose(f);
135
136		if (!found) {
137			return (0);
138		}
139		devname = m.mnt_special;
140	}
141
142	if (devfs_dev_to_prom_name(devname, bdev) != 0) {
143		perror(devname);
144		return (0);
145	}
146
147	return (1);
148}
149
150/*
151 * setprom() - use /dev/openprom to read the "boot_device" variable and set
152 * it to the new value.
153 */
154static int
155setprom(unsigned yes, unsigned verbose, char *bdev)
156{
157	struct openpromio	*pio;
158	int			fd;
159	char			save_bootdev[MAXVALSIZE];
160
161	if ((fd = open("/dev/openprom", O_RDWR)) < 0) {
162		perror(MSGSTR(6002, "Could not open openprom dev"));
163		return (errno);
164	}
165
166	pio = (struct openpromio *)malloc(sizeof (struct openpromio) +
167					MAXVALSIZE + MAXPROPSIZE);
168
169	if (pio == (struct openpromio *)NULL) {
170		perror(MSGSTR(6003, " Error: Unable to allocate memory."));
171		return (errno);
172	}
173
174	pio->oprom_size = MAXVALSIZE;
175	(void) strcpy(pio->oprom_array, BOOTDEV_PROP_NAME);
176
177	if (ioctl(fd, OPROMGETOPT, pio) < 0) {
178		perror(MSGSTR(6004, "openprom getopt ioctl"));
179		return (errno);
180	}
181
182	/*
183	 * save the existing boot-device, so we can use it if setting
184	 * to new value fails.
185	 */
186	(void) strcpy(save_bootdev, pio->oprom_array);
187
188	if (verbose) {
189		(void) fprintf(stdout,
190			MSGSTR(6005,
191			"Current boot-device = %s\n"), pio->oprom_array);
192		(void) fprintf(stdout, MSGSTR(6006,
193			"New boot-device = %s\n"), bdev);
194	}
195
196	if (!yes) {
197		(void) fprintf(stdout, MSGSTR(6007,
198			"Do you want to change boot-device "
199			"to the new setting? (y/n) "));
200		switch (getchar()) {
201			case 'Y':
202			case 'y':
203				break;
204			default:
205				return (0);
206		}
207	}
208
209	/* set the new value for boot-device */
210
211	pio->oprom_size = (int)strlen(BOOTDEV_PROP_NAME) + 1 +
212				(int)strlen(bdev);
213
214	(void) strcpy(pio->oprom_array, BOOTDEV_PROP_NAME);
215	(void) strcpy(pio->oprom_array + (int)strlen(BOOTDEV_PROP_NAME) + 1,
216					bdev);
217
218	if (ioctl(fd, OPROMSETOPT, pio) < 0) {
219		perror(MSGSTR(6008, "openprom setopt ioctl"));
220		return (errno);
221	}
222
223	/* read back the value that was set */
224
225	pio->oprom_size = MAXVALSIZE;
226	(void) strcpy(pio->oprom_array, BOOTDEV_PROP_NAME);
227
228	if (ioctl(fd, OPROMGETOPT, pio) < 0) {
229		perror(MSGSTR(6009, "openprom getopt ioctl"));
230		return (errno);
231	}
232
233	if (strcmp(bdev, pio->oprom_array)) {
234
235		/* could not  set the new device name, set the old one back */
236
237		perror(MSGSTR(6010,
238			"Could not set boot-device, reverting to old value"));
239		pio->oprom_size = (int)strlen(BOOTDEV_PROP_NAME) + 1 +
240			(int)strlen(save_bootdev);
241
242		(void) strcpy(pio->oprom_array, BOOTDEV_PROP_NAME);
243			(void) strcpy(pio->oprom_array +
244				(int)strlen(BOOTDEV_PROP_NAME) + 1,
245				save_bootdev);
246
247		if (ioctl(fd, OPROMSETOPT, pio) < 0) {
248			perror(MSGSTR(6011, "openprom setopt ioctl"));
249			return (errno);
250		}
251
252	}
253
254	(void) close(fd);
255
256	return (0);
257}
258