1/* ----------------------------------------------------------------------- *
2 *
3 *   Copyright 2010 Intel Corp. - All Rights Reserved
4 *
5 *   This program is free software; you can redistribute it and/or modify
6 *   it under the terms of the GNU General Public License as published by
7 *   the Free Software Foundation, Inc., 53 Temple Place Ste 330,
8 *   Boston MA 02111-1307, USA; either version 2 of the License, or
9 *   (at your option) any later version; incorporated herein by reference.
10 *
11 * ----------------------------------------------------------------------- */
12
13/*
14 * syslxopt.c
15 *
16 * parse cmdline for extlinux and syslinux installer
17 *
18 */
19#include <stdio.h>
20#include <stdlib.h>
21#include <stddef.h>
22#include <stdint.h>
23#include <string.h>
24#include <getopt.h>
25#include <sysexits.h>
26#include "version.h"
27#include "syslxcom.h"
28#include "syslxfs.h"
29#include "syslxopt.h"
30
31/* These are the options we can set their values */
32struct sys_options opt = {
33    .sectors = 0,
34    .heads = 0,
35    .raid_mode = 0,
36    .stupid_mode = 0,
37    .reset_adv = 0,
38    .set_once = NULL,
39    .update_only = -1,
40    .directory = NULL,
41    .device = NULL,
42    .offset = 0,
43    .menu_save = NULL,
44    .install_mbr = 0,
45    .activate_partition = 0,
46    .force = 0,
47    .bootsecfile = NULL,
48};
49
50const struct option long_options[] = {
51    {"force", 0, NULL, 'f'},	/* DOS/Win32/mtools only */
52    {"install", 0, NULL, 'i'},
53    {"directory", 1, NULL, 'd'},
54    {"offset", 1, NULL, 't'},
55    {"update", 0, NULL, 'U'},
56    {"zipdrive", 0, NULL, 'z'},
57    {"sectors", 1, NULL, 'S'},
58    {"stupid", 0, NULL, 's'},
59    {"heads", 1, NULL, 'H'},
60    {"raid-mode", 0, NULL, 'r'},
61    {"version", 0, NULL, 'v'},
62    {"help", 0, NULL, 'h'},
63    {"once", 1, NULL, OPT_ONCE},
64    {"clear-once", 0, NULL, 'O'},
65    {"reset-adv", 0, NULL, OPT_RESET_ADV},
66    {"menu-save", 1, NULL, 'M'},
67    {"mbr", 0, NULL, 'm'},	/* DOS/Win32 only */
68    {"active", 0, NULL, 'a'},	/* DOS/Win32 only */
69    {"device", 1, NULL, OPT_DEVICE},
70    {NULL, 0, NULL, 0}
71};
72
73const char short_options[] = "t:fid:UuzsS:H:rvho:OM:ma";
74
75void __attribute__ ((noreturn)) usage(int rv, enum syslinux_mode mode)
76{
77    switch (mode) {
78    case MODE_SYSLINUX:
79	/* For unmounted fs installation (syslinux) */
80	fprintf(stderr,
81	    "Usage: %s [options] device\n"
82	    "  --offset     -t  Offset of the file system on the device \n"
83	    "  --directory  -d  Directory for installation target\n",
84	    program);
85	break;
86
87    case MODE_EXTLINUX:
88	/* Mounted fs installation (extlinux) */
89	/* Actually extlinux can also use -d to provide a directory too... */
90	fprintf(stderr,
91	    "Usage: %s [options] directory\n"
92	    "  --device         Force use of a specific block device (experts only)\n",
93	    program);
94	break;
95
96    case MODE_SYSLINUX_DOSWIN:
97	/* For fs installation under Windows (syslinux.exe) */
98	fprintf(stderr,
99	    "Usage: %s [options] <drive>: [bootsecfile]\n"
100	    "  --directory  -d  Directory for installation target\n",
101	    program);
102	break;
103    }
104
105    fprintf(stderr,
106	    "  --install    -i  Install over the current bootsector\n"
107	    "  --update     -U  Update a previous installation\n"
108	    "  --zip        -z  Force zipdrive geometry (-H 64 -S 32)\n"
109	    "  --sectors=#  -S  Force the number of sectors per track\n"
110	    "  --heads=#    -H  Force number of heads\n"
111	    "  --stupid     -s  Slow, safe and stupid mode\n"
112	    "  --raid       -r  Fall back to the next device on boot failure\n"
113	    "  --once=...   %s  Execute a command once upon boot\n"
114	    "  --clear-once -O  Clear the boot-once command\n"
115	    "  --reset-adv      Reset auxilliary data\n",
116	    mode == MODE_SYSLINUX  ? "  " : "-o");
117    /*
118     * Have to chop this roughly in half for the DOS installer due
119     * to limited output buffer size
120     */
121    fprintf(stderr,
122	    "  --menu-save= -M  Set the label to select as default on the next boot\n");
123    if (mode == MODE_SYSLINUX_DOSWIN)
124	fprintf(stderr,
125		"  --mbr        -m  Install an MBR\n"
126		"  --active     -a  Mark partition as active\n");
127
128    if (mode == MODE_SYSLINUX_DOSWIN || mode == MODE_SYSLINUX)
129	fprintf(stderr,
130		"  --force      -f  Ignore precautions\n");
131
132    exit(rv);
133}
134
135void parse_options(int argc, char *argv[], enum syslinux_mode mode)
136{
137    int o;
138
139    program = argv[0];
140    while ((o = getopt_long(argc, argv, short_options,
141			    long_options, NULL)) != EOF) {
142	switch (o) {
143	case 'f':
144	    opt.force = 1;
145	    break;
146	case 'z':
147	    opt.heads = 64;
148	    opt.sectors = 32;
149	    break;
150	case 'S':
151	    opt.sectors = strtoul(optarg, NULL, 0);
152	    if (opt.sectors < 1 || opt.sectors > 63) {
153		fprintf(stderr,
154			"%s: invalid number of sectors: %u (must be 1-63)\n",
155			program, opt.sectors);
156		exit(EX_USAGE);
157	    }
158	    break;
159	case 'H':
160	    opt.heads = strtoul(optarg, NULL, 0);
161	    if (opt.heads < 1 || opt.heads > 256) {
162		fprintf(stderr,
163			"%s: invalid number of heads: %u (must be 1-256)\n",
164			program, opt.heads);
165		exit(EX_USAGE);
166	    }
167	    break;
168	case 'r':
169	    opt.raid_mode = 1;
170	    break;
171	case 's':
172	    opt.stupid_mode = 1;
173	    break;
174	case 'i':
175	    opt.update_only = 0;
176	    break;
177	case 'u':
178	case 'U':
179	    opt.update_only = 1;
180	    break;
181	case 'h':
182	    usage(0, mode);
183	    break;
184	case 'o':
185	    if (mode == MODE_SYSLINUX) {
186		fprintf(stderr,	"%s: -o will change meaning in a future version, use -t or --offset\n", program);
187		goto opt_offset;
188	    }
189	    /* else fall through */
190	case OPT_ONCE:
191	    opt.set_once = optarg;
192	    break;
193	case 't':
194	opt_offset:
195	    opt.offset = strtoul(optarg, NULL, 0);
196	    break;
197	case 'O':
198	    opt.set_once = "";
199	    break;
200	case 'd':
201	    opt.directory = optarg;
202	    break;
203	case OPT_RESET_ADV:
204	    opt.reset_adv = 1;
205	    break;
206	case 'M':
207	    opt.menu_save = optarg;
208	    break;
209	case 'm':
210	    opt.install_mbr = 1;
211	    break;
212	case 'a':
213	    opt.activate_partition = 1;
214	    break;
215	case OPT_DEVICE:
216	    if (mode != MODE_EXTLINUX)
217		usage(EX_USAGE, mode);
218	    opt.device = optarg;
219	    break;
220	case 'v':
221	    fprintf(stderr,
222		    "%s " VERSION_STR "  Copyright 1994-" YEAR_STR
223		    " H. Peter Anvin et al\n", program);
224	    exit(0);
225	default:
226	    fprintf(stderr, "%s: Unknown option: -%c\n", program, optopt);
227	    usage(EX_USAGE, mode);
228	}
229    }
230
231    switch (mode) {
232    case MODE_SYSLINUX:
233    case MODE_SYSLINUX_DOSWIN:
234	opt.device = argv[optind++];
235	break;
236    case MODE_EXTLINUX:
237	if (!opt.directory)
238	    opt.directory = argv[optind++];
239	break;
240    }
241
242    if (argv[optind] && (mode == MODE_SYSLINUX_DOSWIN))
243	/* Allow for the boot-sector argument */
244	opt.bootsecfile = argv[optind++];
245    if (argv[optind])
246	usage(EX_USAGE, mode);	/* Excess arguments */
247}
248
249/*
250 * Make any user-specified ADV modifications in memory
251 */
252int modify_adv(void)
253{
254    int rv = 0;
255
256    if (opt.reset_adv)
257	syslinux_reset_adv(syslinux_adv);
258
259    if (opt.set_once) {
260	if (syslinux_setadv(ADV_BOOTONCE, strlen(opt.set_once), opt.set_once)) {
261	    fprintf(stderr, "%s: not enough space for boot-once command\n",
262		    program);
263	    rv = -1;
264	}
265    }
266    if (opt.menu_save) {
267        if (syslinux_setadv(ADV_MENUSAVE, strlen(opt.menu_save), opt.menu_save)) {
268	    fprintf(stderr, "%s: not enough space for menu-save label\n",
269		    program);
270	    rv = -1;
271        }
272    }
273
274    return rv;
275}
276