xref: /netbsd-src/sys/modules/examples/panic_string/panic_string.c (revision 76c7fc5f6b13ed0b1508e6b313e88e59977ed78e)
1 /*	$NetBSD: panic_string.c,v 1.1 2018/05/29 16:53:56 kamil Exp $	*/
2 
3 /*-
4  * Copyright (c) 2018 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 
30 #include <sys/cdefs.h>
31 __KERNEL_RCSID(0, "$NetBSD: panic_string.c,v 1.1 2018/05/29 16:53:56 kamil Exp $");
32 
33 #include <sys/param.h>
34 #include <sys/types.h>
35 #include <sys/conf.h>
36 #include <sys/device.h>
37 #include <sys/filedesc.h>
38 #include <sys/kernel.h>
39 #include <sys/kmem.h>
40 #include <sys/lwp.h>
41 #include <sys/module.h>
42 #include <sys/vfs_syscalls.h>
43 
44 /*
45  * Create a device /dev/panic from which you can read sequential
46  * user input.
47  *
48  * To use this device you need to do:
49  *      mknod /dev/panic c 210 0
50  *
51  * To write to the device you might need:
52  *      chmod 666 /dev/panic
53  *
54  * Commentary:
55  * This module manages the device /dev/panic,
56  * tranfers a string from userspace to kernel space
57  * and calls kernel panic with the passed string.
58  *
59  *  echo 'string' > /dev/panic
60  * will do the trick after loading the module.
61  */
62 
63 dev_type_open(panic_string_open);
64 dev_type_close(panic_string_close);
65 dev_type_write(panic_string_write);
66 
67 static struct cdevsw panic_string_cdevsw = {
68 	.d_open = panic_string_open,
69 	.d_close = panic_string_close,
70 	.d_read = noread,
71 	.d_write = panic_string_write,
72 	.d_ioctl = noioctl,
73 	.d_stop = nostop,
74 	.d_tty = notty,
75 	.d_poll = nopoll,
76 	.d_mmap = nommap,
77 	.d_kqfilter = nokqfilter,
78 	.d_discard = nodiscard,
79 	.d_flag = D_OTHER
80 };
81 
82 static struct panic_string_softc {
83 	int refcnt;
84 } sc;
85 
86 /*
87  * A function similar to strnlen + isprint
88  *
89  * Detect length of the printable and non-whitespace string in the buffer.
90  * A string is accepted if it contains any non-space character.
91  */
92 
93 static size_t
94 printable_length(const char *str, size_t len)
95 {
96 	size_t n;
97 	bool accepted;
98 
99 	n = 0;
100 	accepted = false;
101 
102 	while (len > n) {
103 		if (str[n] >= 0x20 && str[n] <= 0x7e) {
104 			if (str[n] != 0x20 /* space */)
105 				accepted = true;
106 			n++;
107 		} else
108 			break;
109 	}
110 
111 	if (accepted)
112 		return n;
113 	else
114 		return 0;
115 }
116 
117 int
118 panic_string_open(dev_t self __unused, int flag __unused, int mod __unused, struct lwp *l)
119 {
120 
121 	/* Make sure the device is opened once at a time */
122 	if (sc.refcnt > 0)
123 		return EBUSY;
124 
125 	++sc.refcnt;
126 
127 	return 0;
128 }
129 
130 int
131 panic_string_close(dev_t self __unused, int flag __unused, int mod __unused, struct lwp *l __unused)
132 {
133 
134 	--sc.refcnt;
135 	return 0;
136 }
137 
138 int
139 panic_string_write(dev_t self, struct uio *uio, int flags)
140 {
141 	size_t len, printlen;
142 	char *buffer;
143 
144 	/* Buffer length */
145 	len = uio->uio_iov->iov_len;
146 
147 	/* Allocate a local buffer to store the string */
148 	buffer = (char *)kmem_alloc(len, KM_SLEEP);
149 
150 	/* Move the string from user to kernel space and store it locally */
151 	uiomove(buffer, len, uio);
152 
153 	printlen = printable_length(buffer, len);
154 
155 	if (printlen > 0) {
156 		/* Flushing disk changes */
157 		do_sys_sync(curlwp);
158 
159 		panic("panic string: %.*s\n", (int)printlen, buffer);
160 //		printf("panic string: %.*s\n", (int)printlen, buffer);
161 
162 		/* NOTREACHED */
163 	}
164 
165 	kmem_free(buffer, len);
166 	return 0;
167 }
168 
169 MODULE(MODULE_CLASS_MISC, panic_string, NULL);
170 
171 static int
172 panic_string_modcmd(modcmd_t cmd, void *arg __unused)
173 {
174 	/* The major should be verified and changed if needed to avoid
175 	 * conflicts with other devices. */
176 	int cmajor = 210, bmajor = -1;
177 
178 	switch (cmd) {
179 	case MODULE_CMD_INIT:
180 		printf("Panic String module loaded.\n");
181 		if (devsw_attach("panic", NULL, &bmajor, &panic_string_cdevsw,
182 						 &cmajor))
183 			return ENXIO;
184 		return 0;
185 
186 	case MODULE_CMD_FINI:
187 		printf("Panic String module unloaded.\n");
188 		if (sc.refcnt > 0)
189 			return EBUSY;
190 
191 		devsw_detach(NULL, &panic_string_cdevsw);
192 		return 0;
193 	default:
194 		return ENOTTY;
195 	}
196 }
197