1/*	$NetBSD: main.c,v 1.26 2021/04/07 14:45:28 simonb Exp $	*/
2
3/*-
4 * Copyright (c) 2008 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#include <sys/cdefs.h>
30#ifndef lint
31__RCSID("$NetBSD: main.c,v 1.26 2021/04/07 14:45:28 simonb Exp $");
32#endif /* !lint */
33
34#include <sys/module.h>
35#include <sys/param.h>
36#include <sys/sysctl.h>
37
38#include <err.h>
39#include <errno.h>
40#include <stdio.h>
41#include <stdlib.h>
42#include <string.h>
43#include <unistd.h>
44#include <stdbool.h>
45
46#include "prog_ops.h"
47
48static void	usage(void) __dead;
49static int	modstatcmp(const void *, const void *);
50
51static const char *classes[] = {
52	"any",
53	"misc",
54	"vfs",
55	"driver",
56	"exec",
57	"secmodel",
58	"bufq"
59};
60const unsigned int class_max = __arraycount(classes);
61
62static const char *sources[] = {
63	"builtin",
64	"boot",
65	"filesys",
66};
67const unsigned int source_max = __arraycount(sources);
68
69static const char *modflags[] = {
70	"-", "f", "a", "af"
71};
72
73int
74main(int argc, char **argv)
75{
76	struct iovec iov;
77	modstat_t *ms;
78	size_t len;
79	const char *name;
80	char sbuf[32];
81	int ch, rc, modauto = 1;
82	size_t maxnamelen = 16, i, modautolen;
83	char loadable = '\0';
84	const char *reqoff, *req;
85	bool address = false;
86
87	name = NULL;
88
89	while ((ch = getopt(argc, argv, "Aaekn:")) != -1) {
90		switch (ch) {
91		case 'A':			/* FALLTHROUGH */
92		case 'a':			/* FALLTHROUGH */
93		case 'e':
94			loadable = (char)ch;
95			break;
96		case 'k':
97			address = true;
98			break;
99		case 'n':
100			name = optarg;
101			break;
102		default:
103			usage();
104			/* NOTREACHED */
105		}
106	}
107
108	argc -= optind;
109	argv += optind;
110	if (argc == 1 && name == NULL)
111		name = argv[0];
112	else if (argc != 0)
113		usage();
114
115	if (prog_init && prog_init() == -1)
116		err(1, "prog init failed");
117
118	if (loadable == 'A' || loadable == 'a') {
119		if (prog_modctl(MODCTL_EXISTS, (void *)(uintptr_t)1)) {
120			switch (errno) {
121			case ENOSYS:
122				errx(EXIT_FAILURE, "The kernel was compiled "
123				    "without options MODULAR.");
124				break;
125			case EPERM:
126				errx(EXIT_FAILURE, "Modules can not be "
127				    "autoloaded right now.");
128				break;
129			default:
130				err(EXIT_FAILURE, "modctl_exists for autoload");
131				break;
132			}
133		} else {
134			if (loadable == 'A') {
135				modautolen = sizeof(modauto);
136				rc = sysctlbyname("kern.module.autoload",
137				    &modauto, &modautolen, NULL, 0);
138				if (rc != 0) {
139					err(EXIT_FAILURE, "sysctl "
140					    "kern.module.autoload failed.");
141				}
142			}
143			errx(EXIT_SUCCESS, "Modules can be autoloaded%s.",
144			modauto ? "" : ", but kern.module.autoload = 0");
145		}
146	}
147
148	if (loadable == 'e') {
149		if (prog_modctl(MODCTL_EXISTS, (void *)(uintptr_t)0)) {
150			switch (errno) {
151			case ENOSYS:
152				errx(EXIT_FAILURE, "The kernel was compiled "
153				    "without options MODULAR.");
154				break;
155			case EPERM:
156				errx(EXIT_FAILURE, "You are not allowed to "
157				    "load modules right now.");
158				break;
159			default:
160				err(EXIT_FAILURE, "modctl_exists for autoload");
161				break;
162			}
163		} else {
164			errx(EXIT_SUCCESS, "You can load modules.");
165		}
166	}
167
168	for (len = 8192;;) {
169		iov.iov_base = malloc(len);
170		iov.iov_len = len;
171		if (prog_modctl(MODCTL_STAT, &iov)) {
172			err(EXIT_FAILURE, "modctl(MODCTL_STAT)");
173		}
174		if (len >= iov.iov_len) {
175			break;
176		}
177		free(iov.iov_base);
178		len = iov.iov_len;
179	}
180
181	len = *(int *)iov.iov_base;
182	ms = (modstat_t *)((char *)iov.iov_base + sizeof(int));
183
184	qsort(ms, len, sizeof(modstat_t), modstatcmp);
185	for (i = 0; i < len; i++, ms++) {
186		size_t namelen = strlen(ms->ms_name);
187		if (maxnamelen < namelen)
188			maxnamelen = namelen;
189	}
190	ms = (modstat_t *)((char *)iov.iov_base + sizeof(int));
191	reqoff = (char *)(&ms[len]);
192
193	printf("%-*s %-8s %-8s %-4s %5s ",
194	    (int)maxnamelen, "NAME", "CLASS", "SOURCE", "FLAG", "REFS");
195	if (address)
196		printf("%-16s ", "ADDRESS");
197	printf("%7s %s \n", "SIZE", "REQUIRES");
198
199	for (; len != 0; ms++, len--) {
200		const char *class;
201		const char *source;
202
203		if (ms->ms_reqoffset == 0)
204			req = "-";
205		else {
206			req = &reqoff[ms->ms_reqoffset];
207		}
208		if (name != NULL && strcmp(ms->ms_name, name) != 0) {
209			continue;
210		}
211		if (ms->ms_size == 0) {
212			sbuf[0] = '-';
213			sbuf[1] = '\0';
214		} else {
215			snprintf(sbuf, sizeof(sbuf), "%u", ms->ms_size);
216		}
217		if (ms->ms_class <= class_max)
218			class = classes[ms->ms_class];
219		else
220			class = "UNKNOWN";
221		if (ms->ms_source < source_max)
222			source = sources[ms->ms_source];
223		else
224			source = "UNKNOWN";
225
226		printf("%-*s %-8s %-8s %-4s %5d ",
227		    (int)maxnamelen, ms->ms_name, class, source,
228		    modflags[ms->ms_flags & (__arraycount(modflags) - 1)],
229		    ms->ms_refcnt);
230		if (address)
231			printf("%-16" PRIx64 " ", ms->ms_addr);
232		printf("%7s %s\n", sbuf, (req));
233	}
234
235	exit(EXIT_SUCCESS);
236}
237
238static void
239usage(void)
240{
241
242	(void)fprintf(stderr, "Usage: %s [-Aaek] [-n name | name]\n",
243	    getprogname());
244	exit(EXIT_FAILURE);
245}
246
247static int
248modstatcmp(const void *a, const void *b)
249{
250	const modstat_t *msa, *msb;
251
252	msa = a;
253	msb = b;
254
255	return strcmp(msa->ms_name, msb->ms_name);
256}
257