1/* $NetBSD: configmenu.c,v 1.5.2.2 2012/05/18 02:28:52 sborrill Exp $ */
2
3/*-
4 * Copyright (c) 2012 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Jeffrey C. Rizzo
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32/* configmenu.c -- post-installation system configuration menu. */
33
34#include <stdio.h>
35#include <curses.h>
36#include <unistd.h>
37#include "defs.h"
38#include "msg_defs.h"
39#include "menu_defs.h"
40
41
42static int set_network(struct menudesc*, void *);
43static int set_timezone_menu(struct menudesc *, void *);
44static int set_root_shell(struct menudesc *, void *);
45static int change_root_password(struct menudesc *, void *);
46static int set_binpkg(struct menudesc *, void *);
47static int set_pkgsrc(struct menudesc *, void *);
48static void config_list_init(void);
49static void get_rootsh(void);
50static int toggle_rcvar(struct menudesc *, void *);
51static void configmenu_hdr(struct menudesc *, void *);
52static int check_root_password(void);
53
54char pkgpath[STRSIZE];
55char pkgsrcpath[STRSIZE];
56
57extern const char *tz_default;
58
59enum {
60	CONFIGOPT_NETCONF,
61	CONFIGOPT_TZ,
62	CONFIGOPT_ROOTSH,
63	CONFIGOPT_ROOTPW,
64	CONFIGOPT_BINPKG,
65	CONFIGOPT_PKGSRC,
66	CONFIGOPT_SSHD,
67	CONFIGOPT_NTPD,
68	CONFIGOPT_NTPDATE,
69	CONFIGOPT_MDNSD,
70	CONFIGOPT_LAST
71};
72
73typedef struct configinfo {
74	const char	*optname;
75	uint		opt;
76	const char	*rcvar;
77	int		(*action)(struct menudesc *, void *);
78	const char	*setting;
79} configinfo;
80
81
82configinfo config_list[] = {
83	{MSG_Configure_network, CONFIGOPT_NETCONF, NULL, set_network, MSG_configure},
84	{MSG_timezone, CONFIGOPT_TZ, NULL, set_timezone_menu, NULL},
85	{MSG_Root_shell, CONFIGOPT_ROOTSH, NULL, set_root_shell, NULL},
86	{MSG_change_rootpw, CONFIGOPT_ROOTPW, NULL, change_root_password, MSG_change},
87	{MSG_enable_binpkg, CONFIGOPT_BINPKG, NULL, set_binpkg, MSG_configure},
88	{MSG_get_pkgsrc, CONFIGOPT_PKGSRC, NULL, set_pkgsrc, MSG_install},
89	{MSG_enable_sshd, CONFIGOPT_SSHD, "sshd", toggle_rcvar, NULL},
90	{MSG_enable_ntpd, CONFIGOPT_NTPD, "ntpd", toggle_rcvar, NULL},
91	{MSG_run_ntpdate, CONFIGOPT_NTPDATE, "ntpdate", toggle_rcvar, NULL},
92	{MSG_enable_mdnsd, CONFIGOPT_MDNSD, "mdnsd", toggle_rcvar, NULL},
93	{NULL,		CONFIGOPT_LAST,	NULL, NULL, NULL}
94};
95
96static void
97config_list_init(void)
98{
99	int i;
100
101	for (i=0; i < CONFIGOPT_LAST; i++) {
102		switch (i) {
103		case CONFIGOPT_TZ:
104			get_tz_default();
105			config_list[CONFIGOPT_TZ].setting = tz_default;
106			break;
107		case CONFIGOPT_ROOTSH:
108			get_rootsh();
109			break;
110		case CONFIGOPT_ROOTPW:
111			if (check_root_password())
112				config_list[i].setting = MSG_password_set;
113			else
114				config_list[i].setting = MSG_empty;
115			break;
116		default:
117			if (config_list[i].rcvar != NULL) {
118				if (check_rcvar(config_list[i].rcvar))
119					config_list[i].setting = MSG_YES;
120				else
121					config_list[i].setting = MSG_NO;
122			}
123			break;
124		}
125	}
126}
127
128static void
129get_rootsh(void)
130{
131	static char *buf = NULL;
132
133	if (buf != NULL)
134		free(buf);
135
136	if (target_already_root())
137		collect(T_OUTPUT, &buf,
138		    "/usr/bin/awk -F: '$1==\"root\" { print $NF; exit }'"
139		    " /etc/passwd");
140	else
141		collect(T_OUTPUT, &buf,
142		    "chroot %s /usr/bin/awk -F: '$1==\"root\" { print $NF; exit }'"
143		    " /etc/passwd",target_prefix());
144
145	config_list[CONFIGOPT_ROOTSH].setting = (const char *)buf;
146}
147
148static void
149set_config(menudesc *menu, int opt, void *arg)
150{
151	configinfo	**configp = arg;
152	configinfo	*config = configp[opt];
153	const char	*optname, *setting;
154
155	optname = config->optname;
156	setting = msg_string(config->setting);
157
158	wprintw(menu->mw, "%-50s %-10s", msg_string(optname), setting);
159}
160
161static int
162init_config_menu(configinfo *conf, menu_ent *me, configinfo **ce)
163{
164	int	opt;
165	int	configopts;
166
167	for (configopts = 0; ; conf++) {
168		opt = conf->opt;
169		if (opt == CONFIGOPT_LAST)
170			break;
171		*ce = conf;
172		me->opt_menu = OPT_NOMENU;
173		me->opt_flags = 0;
174		me->opt_name = NULL;  /* NULL so set_config will draw */
175		me->opt_action = conf->action;
176		configopts++;
177		ce++;
178		me++;
179	}
180
181	return configopts;
182}
183
184static int
185/*ARGSUSED*/
186set_timezone_menu(struct menudesc *menu, void *arg)
187{
188	configinfo **confp = arg;
189	set_timezone();
190	get_tz_default();
191	confp[menu->cursel]->setting = tz_default;
192	return 0;
193}
194
195static int
196set_root_shell(struct menudesc *menu, void *arg)
197{
198	configinfo **confp = arg;
199
200	process_menu(MENU_rootsh, &confp[menu->cursel]->setting);
201	run_program(RUN_PROGRESS | RUN_CHROOT,
202		"chpass -s %s root", confp[menu->cursel]->setting);
203	return 0;
204}
205
206static int
207set_network(struct menudesc *menu, void *arg)
208{
209	network_up = 0;
210	if (config_network())
211		mnt_net_config();
212	return 0;
213}
214
215static int
216check_root_password(void)
217{
218	char *buf;
219	int rval;
220
221	if (target_already_root())
222		collect(T_OUTPUT, &buf, "getent passwd root | cut -d: -f2");
223	else
224		collect(T_OUTPUT, &buf, "chroot %s getent passwd root | "
225		    "chroot %s cut -d: -f2",
226		    target_prefix(), target_prefix());
227
228	if (logfp)
229		fprintf(logfp,"buf %s strlen(buf) %zu\n", buf, strlen(buf));
230
231	if (strlen(buf) <= 1)  /* newline */
232		rval = 0;
233	else
234		rval = 1;
235	free(buf);
236	return rval;
237}
238
239static int
240change_root_password(struct menudesc *menu, void *arg)
241{
242	configinfo **confp = arg;
243
244	msg_display(MSG_rootpw);
245	process_menu(MENU_yesno, NULL);
246	if (yesno)
247		run_program(RUN_DISPLAY | RUN_PROGRESS | RUN_CHROOT,
248			"passwd -l root");
249	confp[menu->cursel]->setting = MSG_password_set;
250	return 0;
251}
252
253static int
254set_binpkg(struct menudesc *menu, void *arg)
255{
256	configinfo **confp = arg;
257
258	char pattern[STRSIZE];
259
260	/* binary pkg config requires network at this point, so if
261	   it's not already configured, do it. */
262	if (network_up == 0) {
263		if (config_network())
264			mnt_net_config();
265	}
266
267	process_menu(MENU_binpkg, NULL);
268	make_url(pkgpath, &pkg, pkg_dir);
269	if ( run_program(RUN_DISPLAY | RUN_PROGRESS | RUN_CHROOT,
270		"pkg_add %s/pkgin", pkgpath) != 0) {
271		msg_display(MSG_pkgin_failed);
272		process_menu(MENU_ok, NULL);
273		confp[menu->cursel]->setting = MSG_failed;
274		return 0;
275	}
276
277	/* configure pkgin to use $pkgpath as a repository */
278	snprintf(pattern, STRSIZE, "s,^[^#].*$,%s,", pkgpath);
279	replace("/usr/pkg/etc/pkgin/repositories.conf", pattern);
280
281	run_program(RUN_DISPLAY | RUN_PROGRESS | RUN_CHROOT,
282		"/usr/pkg/bin/pkgin -y update");
283
284	msg_display(MSG_binpkg_installed);
285	process_menu(MENU_ok, NULL);
286
287	confp[menu->cursel]->setting = MSG_DONE;
288	return 0;
289}
290
291static int
292set_pkgsrc(struct menudesc *menu, void *arg)
293{
294	configinfo **confp = arg;
295	distinfo dist;
296
297	dist.name = "pkgsrc";
298	dist.set = SET_PKGSRC;
299	dist.desc = "source for 3rd-party packages";
300	dist.marker_file = NULL;
301
302	int status = SET_RETRY;
303
304	do {
305		status = get_pkgsrc();
306		if (status == SET_OK) {
307			status = extract_file(&dist, 0);
308			continue;
309		} else if (status == SET_SKIP) {
310			confp[menu->cursel]->setting = MSG_abandoned;
311			return 0;
312		}
313		process_menu(MENU_yesno, deconst(MSG_retry_pkgsrc_network));
314		if (!yesno) {
315			confp[menu->cursel]->setting = MSG_abandoned;
316			return 1;
317		}
318	}
319	while (status == SET_RETRY);
320
321
322	confp[menu->cursel]->setting = MSG_DONE;
323	return 0;
324}
325
326static int
327toggle_rcvar(struct menudesc *menu, void *arg)
328{
329	configinfo **confp = arg;
330	int s;
331	const char *setting, *varname;
332	char pattern[STRSIZE];
333	char buf[STRSIZE];
334	char *cp;
335	int found = 0;
336	FILE *fp;
337
338	varname = confp[menu->cursel]->rcvar;
339
340	s = check_rcvar(varname);
341
342	/* we're toggling, so invert the sense */
343	if (s) {
344		confp[menu->cursel]->setting = MSG_NO;
345		setting = "NO";
346	} else {
347		confp[menu->cursel]->setting = MSG_YES;
348		setting = "YES";
349	}
350
351	if (!(fp = fopen(target_expand("/etc/rc.conf"), "r"))) {
352		msg_display(MSG_rcconf_delete_failed, varname);
353		process_menu(MENU_ok, NULL);
354		return -1;
355	}
356
357	while (fgets(buf, sizeof buf, fp) != NULL) {
358		cp = buf + strspn(buf, " \t"); /* Skip initial spaces */
359		if (strncmp(cp, varname, strlen(varname)) == 0) {
360			cp += strlen(varname);
361			if (*cp != '=')
362				continue;
363			buf[strlen(buf) - 1] = 0;
364			snprintf(pattern, sizeof pattern,
365					"s,^%s$,%s=%s,",
366					buf, varname, setting);
367			found = 1;
368			break;
369		}
370	}
371
372	fclose(fp);
373
374	if (!found) {
375		add_rc_conf("%s=%s\n", varname, setting);
376		if (logfp) {
377			fprintf(logfp, "adding %s=%s\n", varname, setting);
378			fflush(logfp);
379		}
380	} else {
381		if (logfp) {
382			fprintf(logfp, "replacement pattern is %s\n", pattern);
383			fflush(logfp);
384		}
385		replace("/etc/rc.conf", pattern);
386	}
387
388	return 0;
389}
390
391static void
392configmenu_hdr(struct menudesc *menu, void *arg)
393{
394	msg_display(MSG_configmenu);
395}
396
397void
398do_configmenu()
399{
400	int		menu_no;
401	int		opts;
402	menu_ent	me[CONFIGOPT_LAST];
403	configinfo	*ce[CONFIGOPT_LAST];
404
405        wrefresh(curscr);
406        wmove(stdscr, 0, 0);
407        wclear(stdscr);
408        wrefresh(stdscr);
409
410	/* if the target isn't mounted already, figure it out. */
411	if (target_mounted() == 0) {
412		if (find_disks(msg_string(MSG_configure_prior)) < 0)
413			return;
414
415		if (mount_disks() != 0)
416			return;
417	}
418
419	config_list_init();
420	make_url(pkgpath, &pkg, pkg_dir);
421	opts = init_config_menu(config_list, me, ce);
422
423	menu_no = new_menu(NULL, me, opts, 0, -4, 0, 70,
424		MC_SCROLL | MC_NOBOX | MC_DFLTEXIT,
425		configmenu_hdr, set_config, NULL, "XXX Help String",
426		MSG_doneconfig);
427
428	process_menu(menu_no, ce);
429	free_menu(menu_no);
430
431	sanity_check();
432
433}
434