Home | History | Annotate | Line # | Download | only in panic_string
panic_string.c revision 1.1.4.1
      1 /*	$NetBSD: panic_string.c,v 1.1.4.1 2020/04/08 14:08:55 martin 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.4.1 2020/04/08 14:08:55 martin 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 351 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 = 351, 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