minix/minix/usr.bin/trace/ioctl/char.c

600 lines
14 KiB
C
Raw Normal View History

#include "inc.h"
#include <dev/pci/pciio.h>
#include <minix/fb.h>
#include <minix/i2c.h>
#include <minix/keymap.h>
#include <minix/sound.h>
#include <sys/fcntl.h>
#include <sys/ioctl.h>
#include <sys/kbdio.h>
#include <sys/termios.h>
#include <sys/time.h>
const char *
char_ioctl_name(unsigned long req)
{
switch (req) {
NAME(MINIX_I2C_IOCTL_EXEC);
NAME(FBIOGET_VSCREENINFO);
NAME(FBIOPUT_VSCREENINFO);
NAME(FBIOGET_FSCREENINFO); /* TODO: print argument */
NAME(FBIOPAN_DISPLAY);
NAME(DSPIORATE);
NAME(DSPIOSTEREO);
NAME(DSPIOSIZE);
NAME(DSPIOBITS);
NAME(DSPIOSIGN);
NAME(DSPIOMAX);
NAME(DSPIORESET); /* no argument */
NAME(DSPIOFREEBUF);
NAME(DSPIOSAMPLESINBUF);
NAME(DSPIOPAUSE); /* no argument */
NAME(DSPIORESUME); /* no argument */
NAME(MIXIOGETVOLUME);
NAME(MIXIOGETINPUTLEFT);
NAME(MIXIOGETINPUTRIGHT);
NAME(MIXIOGETOUTPUT);
NAME(MIXIOSETVOLUME);
NAME(MIXIOSETINPUTLEFT);
NAME(MIXIOSETINPUTRIGHT);
NAME(MIXIOSETOUTPUT);
NAME(TIOCEXCL); /* no argument */
NAME(TIOCNXCL); /* no argument */
NAME(TIOCFLUSH);
NAME(TIOCGETA);
NAME(TIOCSETA);
NAME(TIOCSETAW);
NAME(TIOCSETAF);
NAME(TIOCGETD);
NAME(TIOCSETD);
NAME(TIOCGLINED);
NAME(TIOCSLINED);
NAME(TIOCSBRK); /* no argument */
NAME(TIOCCBRK); /* no argument */
NAME(TIOCSDTR); /* no argument */
NAME(TIOCCDTR); /* no argument */
NAME(TIOCGPGRP);
NAME(TIOCSPGRP);
NAME(TIOCOUTQ);
NAME(TIOCSTI);
NAME(TIOCNOTTY); /* no argument */
NAME(TIOCPKT);
NAME(TIOCSTOP); /* no argument */
NAME(TIOCSTART); /* no argument */
NAME(TIOCMSET); /* TODO: print argument */
NAME(TIOCMBIS); /* TODO: print argument */
NAME(TIOCMBIC); /* TODO: print argument */
NAME(TIOCMGET); /* TODO: print argument */
NAME(TIOCREMOTE);
NAME(TIOCGWINSZ);
NAME(TIOCSWINSZ);
NAME(TIOCUCNTL);
NAME(TIOCSTAT);
NAME(TIOCGSID);
NAME(TIOCCONS);
NAME(TIOCSCTTY); /* no argument */
NAME(TIOCEXT);
NAME(TIOCSIG); /* no argument */
NAME(TIOCDRAIN); /* no argument */
NAME(TIOCGFLAGS); /* TODO: print argument */
NAME(TIOCSFLAGS); /* TODO: print argument */
NAME(TIOCDCDTIMESTAMP); /* TODO: print argument */
NAME(TIOCRCVFRAME); /* TODO: print argument */
NAME(TIOCXMTFRAME); /* TODO: print argument */
NAME(TIOCPTMGET); /* TODO: print argument */
NAME(TIOCGRANTPT); /* no argument */
NAME(TIOCPTSNAME); /* TODO: print argument */
NAME(TIOCSQSIZE);
NAME(TIOCGQSIZE);
NAME(TIOCSFON); /* big IOCTL, not printing argument */
NAME(KIOCBELL);
NAME(KIOCSLEDS);
NAME(KIOCSMAP); /* not worth interpreting */
NAME(PCI_IOC_CFGREAD);
NAME(PCI_IOC_CFGWRITE);
NAME(PCI_IOC_BDF_CFGREAD);
NAME(PCI_IOC_BDF_CFGWRITE);
NAME(PCI_IOC_BUSINFO);
NAME(PCI_IOC_MAP);
NAME(PCI_IOC_UNMAP);
NAME(PCI_IOC_RESERVE);
NAME(PCI_IOC_RELEASE);
}
return NULL;
}
static void
put_i2c_op(struct trace_proc * proc, const char *name, i2c_op_t op)
{
const char *text = NULL;
if (!valuesonly) {
switch (op) {
TEXT(I2C_OP_READ);
TEXT(I2C_OP_READ_WITH_STOP);
TEXT(I2C_OP_WRITE);
TEXT(I2C_OP_WRITE_WITH_STOP);
TEXT(I2C_OP_READ_BLOCK);
TEXT(I2C_OP_WRITE_BLOCK);
}
}
if (text != NULL)
put_field(proc, name, text);
else
put_value(proc, name, "%d", op);
}
static void
put_sound_device(struct trace_proc * proc, const char * name, int device)
{
const char *text = NULL;
if (!valuesonly) {
switch (device) {
TEXT(Master);
TEXT(Dac);
TEXT(Fm);
TEXT(Cd);
TEXT(Line);
TEXT(Mic);
TEXT(Speaker);
TEXT(Treble);
TEXT(Bass);
}
}
if (text != NULL)
put_field(proc, name, text);
else
put_value(proc, name, "%d", device);
}
static void
put_sound_state(struct trace_proc * proc, const char * name, int state)
{
if (!valuesonly && state == ON)
put_field(proc, name, "ON");
else if (!valuesonly && state == OFF)
put_field(proc, name, "OFF");
else
put_value(proc, name, "%d", state);
}
static const struct flags flush_flags[] = {
FLAG(FREAD),
FLAG(FWRITE),
};
static const struct flags tc_iflags[] = {
FLAG(IGNBRK),
FLAG(BRKINT),
FLAG(IGNPAR),
FLAG(PARMRK),
FLAG(INPCK),
FLAG(ISTRIP),
FLAG(INLCR),
FLAG(IGNCR),
FLAG(ICRNL),
FLAG(IXON),
FLAG(IXOFF),
FLAG(IXANY),
FLAG(IMAXBEL),
};
static const struct flags tc_oflags[] = {
FLAG(OPOST),
FLAG(ONLCR),
FLAG(OXTABS),
FLAG(ONOEOT),
FLAG(OCRNL),
FLAG(ONOCR),
FLAG(ONLRET),
};
static const struct flags tc_cflags[] = {
FLAG(CIGNORE),
FLAG_MASK(CSIZE, CS5),
FLAG_MASK(CSIZE, CS6),
FLAG_MASK(CSIZE, CS7),
FLAG_MASK(CSIZE, CS8),
FLAG(CSTOPB),
FLAG(CREAD),
FLAG(PARENB),
FLAG(PARODD),
FLAG(HUPCL),
FLAG(CLOCAL),
FLAG(CRTSCTS),
FLAG(CDTRCTS),
FLAG(MDMBUF),
};
static const struct flags tc_lflags[] = {
FLAG(ECHOKE),
FLAG(ECHOE),
FLAG(ECHOK),
FLAG(ECHO),
FLAG(ECHONL),
FLAG(ECHOPRT),
FLAG(ECHOCTL),
FLAG(ISIG),
FLAG(ICANON),
FLAG(ALTWERASE),
FLAG(IEXTEN),
FLAG(EXTPROC),
FLAG(TOSTOP),
FLAG(FLUSHO),
FLAG(NOKERNINFO),
FLAG(PENDIN),
FLAG(NOFLSH),
};
static void
put_tty_disc(struct trace_proc * proc, const char * name, int disc)
{
const char *text = NULL;
if (!valuesonly) {
switch (disc) {
TEXT(TTYDISC);
TEXT(TABLDISC);
TEXT(SLIPDISC);
TEXT(PPPDISC);
TEXT(STRIPDISC);
TEXT(HDLCDISC);
}
}
if (text != NULL)
put_field(proc, name, text);
else
put_value(proc, name, "%d", disc);
}
static const struct flags kbd_leds[] = {
FLAG(KBD_LEDS_NUM),
FLAG(KBD_LEDS_CAPS),
FLAG(KBD_LEDS_SCROLL),
};
int
char_ioctl_arg(struct trace_proc * proc, unsigned long req, void * ptr,
int dir)
{
minix_i2c_ioctl_exec_t *iie;
struct fb_var_screeninfo *fbvs;
struct volume_level *level;
struct inout_ctrl *inout;
struct termios *tc;
Add PTYFS, Unix98 pseudo terminal support This patch adds support for Unix98 pseudo terminals, that is, posix_openpt(3), grantpt(3), unlockpt(3), /dev/ptmx, and /dev/pts/. The latter is implemented with a new pseudo file system, PTYFS. In effect, this patch adds secure support for unprivileged pseudo terminal allocation, allowing programs such as tmux(1) to be used by non-root users as well. Test77 has been extended with new tests, and no longer needs to run as root. The new functionality is optional. To revert to the old behavior, remove the "ptyfs" entry from /etc/fstab. Technical nodes: o The reason for not implementing the NetBSD /dev/ptm approach is that implementing the corresponding ioctl (TIOCPTMGET) would require adding a number of extremely hairy exceptions to VFS, including the PTY driver having to create new file descriptors for its own device nodes. o PTYFS is required for Unix98 PTYs in order to avoid that the PTY driver has to be aware of old-style PTY naming schemes and even has to call chmod(2) on a disk-backed file system. PTY cannot be its own PTYFS since a character driver may currently not also be a file system. However, PTYFS may be subsumed into a DEVFS in the future. o The Unix98 PTY behavior differs somewhat from NetBSD's, in that slave nodes are created on ptyfs only upon the first call to grantpt(3). This approach obviates the need to revoke access as part of the grantpt(3) call. o Shutting down PTY may leave slave nodes on PTYFS, but once PTY is restarted, these leftover slave nodes will be removed before they create a security risk. Unmounting PTYFS will make existing PTY slaves permanently unavailable, and absence of PTYFS will block allocation of new Unix98 PTYs until PTYFS is (re)mounted. Change-Id: I822b43ba32707c8815fd0f7d5bb7a438f51421c1
2015-06-22 19:14:34 +02:00
struct ptmget *pm;
struct winsize *ws;
struct kio_bell *bell;
struct kio_leds *leds;
struct pciio_cfgreg *pci_cfgreg;
struct pciio_bdf_cfgreg *pci_bdf_cfgreg;
struct pciio_businfo *pci_businfo;
struct pciio_map *pci_iomap;
struct pciio_acl *pci_acl;
switch (req) {
case MINIX_I2C_IOCTL_EXEC:
if ((iie = (minix_i2c_ioctl_exec_t *)ptr) == NULL)
return IF_OUT; /* we print only the request for now */
put_i2c_op(proc, "iie_op", iie->iie_op);
put_value(proc, "iie_addr", "0x%04x", iie->iie_addr);
return 0; /* TODO: print command/data/result */
case FBIOGET_VSCREENINFO:
if ((fbvs = (struct fb_var_screeninfo *)ptr) == NULL)
return IF_IN;
put_value(proc, "xres", "%"PRIu32, fbvs->xres);
put_value(proc, "yres", "%"PRIu32, fbvs->yres);
put_value(proc, "xres_virtual", "%"PRIu32, fbvs->xres_virtual);
put_value(proc, "yres_virtual", "%"PRIu32, fbvs->yres_virtual);
put_value(proc, "xoffset", "%"PRIu32, fbvs->xoffset);
put_value(proc, "yoffset", "%"PRIu32, fbvs->yoffset);
put_value(proc, "bits_per_pixel", "%"PRIu32,
fbvs->bits_per_pixel);
return 0;
case FBIOPUT_VSCREENINFO:
case FBIOPAN_DISPLAY:
if ((fbvs = (struct fb_var_screeninfo *)ptr) == NULL)
return IF_OUT;
put_value(proc, "xoffset", "%"PRIu32, fbvs->xoffset);
put_value(proc, "yoffset", "%"PRIu32, fbvs->yoffset);
return 0;
case DSPIORATE:
case DSPIOSTEREO:
case DSPIOSIZE:
case DSPIOBITS:
case DSPIOSIGN:
case DSPIOMAX:
case DSPIOFREEBUF:
case DSPIOSAMPLESINBUF:
if (ptr == NULL)
return dir;
put_value(proc, NULL, "%u", *(unsigned int *)ptr);
return IF_ALL;
case MIXIOGETVOLUME:
if ((level = (struct volume_level *)ptr) == NULL)
return dir;
if (dir == IF_OUT)
put_sound_device(proc, "device", level->device);
else {
put_value(proc, "left", "%d", level->left);
put_value(proc, "right", "%d", level->right);
}
return IF_ALL;
case MIXIOSETVOLUME:
/* Print the corrected volume levels only with verbosity on. */
if ((level = (struct volume_level *)ptr) == NULL)
return IF_OUT | ((verbose > 0) ? IF_IN : 0);
if (dir == IF_OUT)
put_sound_device(proc, "device", level->device);
put_value(proc, "left", "%d", level->left);
put_value(proc, "right", "%d", level->right);
return IF_ALL;
case MIXIOGETINPUTLEFT:
case MIXIOGETINPUTRIGHT:
case MIXIOGETOUTPUT:
if ((inout = (struct inout_ctrl *)ptr) == NULL)
return dir;
if (dir == IF_OUT)
put_sound_device(proc, "device", inout->device);
else {
put_sound_state(proc, "left", inout->left);
put_sound_state(proc, "right", inout->right);
}
return IF_ALL;
case MIXIOSETINPUTLEFT:
case MIXIOSETINPUTRIGHT:
case MIXIOSETOUTPUT:
if ((inout = (struct inout_ctrl *)ptr) == NULL)
return IF_OUT;
put_sound_device(proc, "device", inout->device);
put_sound_state(proc, "left", inout->left);
put_sound_state(proc, "right", inout->right);
return IF_ALL;
case TIOCFLUSH:
if (ptr == NULL)
return IF_OUT;
put_flags(proc, NULL, flush_flags, COUNT(flush_flags), "0x%x",
*(int *)ptr);
return IF_ALL;
case TIOCGETA:
case TIOCSETA:
case TIOCSETAW:
case TIOCSETAF:
if ((tc = (struct termios *)ptr) == NULL)
return dir;
/*
* These are fairly common IOCTLs, so printing everything by
* default would create a lot of noise. By default we limit
* ourselves to printing the field that contains what I
* consider to be the most important flag: ICANON.
* TODO: see if we can come up with a decent format for
* selectively printing (relatively important) flags.
*/
if (verbose > 0) {
put_flags(proc, "c_iflag", tc_iflags, COUNT(tc_iflags),
"0x%x", tc->c_iflag);
put_flags(proc, "c_oflag", tc_oflags, COUNT(tc_oflags),
"0x%x", tc->c_oflag);
put_flags(proc, "c_cflag", tc_cflags, COUNT(tc_cflags),
"0x%x", tc->c_cflag);
}
put_flags(proc, "c_lflag", tc_lflags, COUNT(tc_lflags), "0x%x",
tc->c_lflag);
if (verbose > 0) {
put_value(proc, "c_ispeed", "%d", tc->c_ispeed);
put_value(proc, "c_ospeed", "%d", tc->c_ospeed);
}
return 0; /* TODO: print the c_cc fields */
case TIOCGETD:
case TIOCSETD:
if (ptr == NULL)
return dir;
put_tty_disc(proc, NULL, *(int *)ptr);
return IF_ALL;
case TIOCGLINED:
case TIOCSLINED:
if (ptr == NULL)
return dir;
put_buf(proc, NULL, PF_LOCADDR | PF_STRING, (vir_bytes)ptr,
sizeof(linedn_t));
return IF_ALL;
case TIOCGPGRP:
case TIOCSPGRP:
case TIOCOUTQ:
case TIOCPKT:
case TIOCREMOTE:
case TIOCUCNTL:
case TIOCSTAT: /* argument seems unused? */
case TIOCGSID:
case TIOCCONS: /* argument seems unused? */
case TIOCEXT:
case TIOCSQSIZE:
case TIOCGQSIZE:
/* Print a simple integer. */
if (ptr == NULL)
return dir;
put_value(proc, NULL, "%d", *(int *)ptr);
return IF_ALL;
Add PTYFS, Unix98 pseudo terminal support This patch adds support for Unix98 pseudo terminals, that is, posix_openpt(3), grantpt(3), unlockpt(3), /dev/ptmx, and /dev/pts/. The latter is implemented with a new pseudo file system, PTYFS. In effect, this patch adds secure support for unprivileged pseudo terminal allocation, allowing programs such as tmux(1) to be used by non-root users as well. Test77 has been extended with new tests, and no longer needs to run as root. The new functionality is optional. To revert to the old behavior, remove the "ptyfs" entry from /etc/fstab. Technical nodes: o The reason for not implementing the NetBSD /dev/ptm approach is that implementing the corresponding ioctl (TIOCPTMGET) would require adding a number of extremely hairy exceptions to VFS, including the PTY driver having to create new file descriptors for its own device nodes. o PTYFS is required for Unix98 PTYs in order to avoid that the PTY driver has to be aware of old-style PTY naming schemes and even has to call chmod(2) on a disk-backed file system. PTY cannot be its own PTYFS since a character driver may currently not also be a file system. However, PTYFS may be subsumed into a DEVFS in the future. o The Unix98 PTY behavior differs somewhat from NetBSD's, in that slave nodes are created on ptyfs only upon the first call to grantpt(3). This approach obviates the need to revoke access as part of the grantpt(3) call. o Shutting down PTY may leave slave nodes on PTYFS, but once PTY is restarted, these leftover slave nodes will be removed before they create a security risk. Unmounting PTYFS will make existing PTY slaves permanently unavailable, and absence of PTYFS will block allocation of new Unix98 PTYs until PTYFS is (re)mounted. Change-Id: I822b43ba32707c8815fd0f7d5bb7a438f51421c1
2015-06-22 19:14:34 +02:00
case TIOCPTSNAME:
if ((pm = (struct ptmget *)ptr) == NULL)
return IF_IN;
put_buf(proc, "sn", PF_LOCADDR | PF_STRING, (vir_bytes)pm->sn,
sizeof(pm->sn));
return IF_ALL;
case TIOCSTI:
if (ptr == NULL)
return dir;
if (!valuesonly)
put_value(proc, NULL, "'%s'",
get_escape(*(char *)ptr));
else
put_value(proc, NULL, "%u", *(char *)ptr);
return IF_ALL;
case TIOCGWINSZ:
case TIOCSWINSZ:
if ((ws = (struct winsize *)ptr) == NULL)
return dir;
/* This is a stupid order, but we follow the struct layout. */
put_value(proc, "ws_row", "%u", ws->ws_row);
put_value(proc, "ws_col", "%u", ws->ws_col);
if (verbose > 0) {
put_value(proc, "ws_xpixel", "%u", ws->ws_xpixel);
put_value(proc, "ws_ypixel", "%u", ws->ws_ypixel);
}
return (verbose > 0) ? IF_ALL : 0;
case KIOCBELL:
if ((bell = (struct kio_bell *)ptr) == NULL)
return IF_OUT;
put_value(proc, "kb_pitch", "%u", bell->kb_pitch);
put_value(proc, "kb_volume", "%lu", bell->kb_volume);
put_struct_timeval(proc, "kb_duration", PF_LOCADDR,
(vir_bytes)&bell->kb_duration);
return IF_ALL;
case KIOCSLEDS:
if ((leds = (struct kio_leds *)ptr) == NULL)
return IF_OUT;
put_flags(proc, "kl_bits", kbd_leds, COUNT(kbd_leds), "0x%x",
leds->kl_bits);
return IF_ALL;
case PCI_IOC_CFGREAD:
if ((pci_cfgreg = (struct pciio_cfgreg *)ptr) == NULL)
return IF_IN;
put_ptr(proc, "reg", (vir_bytes)pci_cfgreg->reg);
put_value(proc, "val", "%08x", pci_cfgreg->val);
return IF_ALL;
case PCI_IOC_CFGWRITE:
if ((pci_cfgreg = (struct pciio_cfgreg *)ptr) == NULL)
return IF_OUT;
put_ptr(proc, "reg", (vir_bytes)pci_cfgreg->reg);
put_value(proc, "val", "%08x", pci_cfgreg->val);
return IF_ALL;
case PCI_IOC_BDF_CFGREAD:
if ((pci_bdf_cfgreg = (struct pciio_bdf_cfgreg *)ptr) == NULL)
return IF_IN;
put_value(proc, "bus", "%u", pci_bdf_cfgreg->bus);
put_value(proc, "device", "%u", pci_bdf_cfgreg->device);
put_value(proc, "function", "%u", pci_bdf_cfgreg->function);
put_ptr(proc, "cfgreg.reg", (vir_bytes)pci_bdf_cfgreg->cfgreg.reg);
put_value(proc, "cfgreg.val", "%08x", pci_bdf_cfgreg->cfgreg.val);
return IF_ALL;
case PCI_IOC_BDF_CFGWRITE:
if ((pci_bdf_cfgreg = (struct pciio_bdf_cfgreg *)ptr) == NULL)
return IF_OUT;
put_value(proc, "bus", "%u", pci_bdf_cfgreg->bus);
put_value(proc, "device", "%u", pci_bdf_cfgreg->device);
put_value(proc, "function", "%u", pci_bdf_cfgreg->function);
put_ptr(proc, "cfgreg.reg", (vir_bytes)pci_bdf_cfgreg->cfgreg.reg);
put_value(proc, "cfgreg.val", "%08x", pci_bdf_cfgreg->cfgreg.val);
return IF_ALL;
case PCI_IOC_BUSINFO:
if ((pci_businfo = (struct pciio_businfo *)ptr) == NULL)
return IF_IN;
put_value(proc, "busno", "%u", pci_businfo->busno);
put_value(proc, "maxdevs", "%u", pci_businfo->maxdevs);
return IF_ALL;
case PCI_IOC_MAP:
if ((pci_iomap = (struct pciio_map *)ptr) == NULL)
return IF_OUT|IF_IN;
put_value(proc, "flags", "%x", pci_iomap->flags);
put_value(proc, "phys_offset", "%08x", pci_iomap->phys_offset);
put_value(proc, "size", "%zu", pci_iomap->size);
put_value(proc, "readonly", "%x", pci_iomap->readonly);
if (IF_IN == dir)
put_ptr(proc, "vaddr_ret", (vir_bytes)pci_iomap->vaddr_ret);
return IF_ALL;
case PCI_IOC_UNMAP:
if ((pci_iomap = (struct pciio_map *)ptr) == NULL)
return IF_OUT;
put_ptr(proc, "vaddr", (vir_bytes)pci_iomap->vaddr);
return IF_ALL;
case PCI_IOC_RESERVE:
if ((pci_acl = (struct pciio_acl *)ptr) == NULL)
return IF_OUT;
put_value(proc, "domain", "%u", pci_acl->domain);
put_value(proc, "bus", "%u", pci_acl->bus);
put_value(proc, "device", "%u", pci_acl->device);
put_value(proc, "function", "%u", pci_acl->function);
return IF_ALL;
case PCI_IOC_RELEASE:
if ((pci_acl = (struct pciio_acl *)ptr) == NULL)
return IF_OUT;
put_value(proc, "domain", "%u", pci_acl->domain);
put_value(proc, "bus", "%u", pci_acl->bus);
put_value(proc, "device", "%u", pci_acl->device);
put_value(proc, "function", "%u", pci_acl->function);
return IF_ALL;
default:
return 0;
}
}