1/*-
2 * Copyright (c) 1997 Doug Rabson
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 AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, 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/cdefs.h>
28__FBSDID("$FreeBSD$");
29
30#include <sys/types.h>
31#include <sys/param.h>
32#include <sys/linker.h>
33#include <sys/sysctl.h>
34#include <sys/stat.h>
35#include <err.h>
36#include <stdio.h>
37#include <stdlib.h>
38#include <string.h>
39#include <unistd.h>
40#include <errno.h>
41
42#define	PATHCTL	"kern.module_path"
43
44static int	path_check(const char *, int);
45static void	usage(void);
46
47/*
48 * Check to see if the requested module is specified as a filename with no
49 * path.  If so and if a file by the same name exists in the module path,
50 * warn the user that the module in the path will be used in preference.
51 */
52static int
53path_check(const char *kldname, int quiet)
54{
55	int	mib[5], found;
56	size_t	miblen, pathlen;
57	char	kldpath[MAXPATHLEN];
58	char	*path, *tmppath, *element;
59	struct	stat sb;
60	dev_t	dev;
61	ino_t	ino;
62
63	if (strchr(kldname, '/') != NULL) {
64		return (0);
65	}
66	if (strstr(kldname, ".ko") == NULL) {
67		return (0);
68	}
69	if (stat(kldname, &sb) != 0) {
70		return (0);
71	}
72
73	found = 0;
74	dev = sb.st_dev;
75	ino = sb.st_ino;
76
77	miblen = sizeof(mib) / sizeof(mib[0]);
78	if (sysctlnametomib(PATHCTL, mib, &miblen) != 0) {
79		err(1, "sysctlnametomib(%s)", PATHCTL);
80	}
81	if (sysctl(mib, miblen, NULL, &pathlen, NULL, 0) == -1) {
82		err(1, "getting path: sysctl(%s) - size only", PATHCTL);
83	}
84	path = malloc(pathlen + 1);
85	if (path == NULL) {
86		err(1, "allocating %lu bytes for the path",
87		    (unsigned long)pathlen + 1);
88	}
89	if (sysctl(mib, miblen, path, &pathlen, NULL, 0) == -1) {
90		err(1, "getting path: sysctl(%s)", PATHCTL);
91	}
92	tmppath = path;
93
94	while ((element = strsep(&tmppath, ";")) != NULL) {
95		strlcpy(kldpath, element, MAXPATHLEN);
96		if (kldpath[strlen(kldpath) - 1] != '/') {
97			strlcat(kldpath, "/", MAXPATHLEN);
98		}
99		strlcat(kldpath, kldname, MAXPATHLEN);
100
101		if (stat(kldpath, &sb) == -1) {
102			continue;
103		}
104
105		found = 1;
106
107		if (sb.st_dev != dev || sb.st_ino != ino) {
108			if (!quiet) {
109				warnx("%s will be loaded from %s, not the "
110				    "current directory", kldname, element);
111			}
112			break;
113		} else if (sb.st_dev == dev && sb.st_ino == ino) {
114			break;
115		}
116	}
117
118	free(path);
119
120	if (!found) {
121		if (!quiet) {
122			warnx("%s is not in the module path", kldname);
123		}
124		return (-1);
125	}
126
127	return (0);
128}
129
130static void
131usage(void)
132{
133	fprintf(stderr, "usage: kldload [-nqv] file ...\n");
134	exit(1);
135}
136
137int
138main(int argc, char** argv)
139{
140	int c;
141	int errors;
142	int fileid;
143	int verbose;
144	int quiet;
145	int check_loaded;
146
147	errors = 0;
148	verbose = 0;
149	quiet = 0;
150	check_loaded = 0;
151
152	while ((c = getopt(argc, argv, "nqv")) != -1) {
153		switch (c) {
154		case 'q':
155			quiet = 1;
156			verbose = 0;
157			break;
158		case 'v':
159			verbose = 1;
160			quiet = 0;
161			break;
162		case 'n':
163			check_loaded = 1;
164			break;
165		default:
166			usage();
167		}
168	}
169	argc -= optind;
170	argv += optind;
171
172	if (argc == 0)
173		usage();
174
175	while (argc-- != 0) {
176		if (path_check(argv[0], quiet) == 0) {
177			fileid = kldload(argv[0]);
178			if (fileid < 0) {
179				if (check_loaded != 0 && errno == EEXIST) {
180					if (verbose)
181						printf("%s is already "
182						    "loaded\n", argv[0]);
183				} else {
184					switch (errno) {
185					case EEXIST:
186						warnx("can't load %s: module "
187						    "already loaded or "
188						    "in kernel", argv[0]);
189						break;
190					case ENOEXEC:
191						warnx("an error occurred while "
192						    "loading the module. "
193						    "Please check dmesg(8) for "
194						    "more details.");
195						break;
196					default:
197						warn("can't load %s", argv[0]);
198						break;
199					}
200					errors++;
201				}
202			} else {
203				if (verbose)
204					printf("Loaded %s, id=%d\n", argv[0],
205					    fileid);
206			}
207		} else {
208			errors++;
209		}
210		argv++;
211	}
212
213	return (errors ? 1 : 0);
214}
215