Console IOCTLs Under Linux -------------------------- Shok (Matt Conover), shok@dataforce.net Console IOCTLs can be very useful and powerful. These are the IOCTls that involve the console. They are the user interface to manipulation of the console. I am going to go over these console IOCTLs and give you examples of them. You can make some pretty powerful programs, whether they be general utilities or security programs, with these (such as Auto Console Switching and Console Access Protection). The structure of this article will be the name of the IOCTL, and then example source code to uses of the IOCTL. Now, I must warn you that the console IOCTLs are being replaced by POSIX functions. These IOCTLs are undocumented Linux internals and can be added or dropped without warning. I did not include all the console IOCTLs, but I included the ones that I though would be important or useful. The switch statement for each of these IOCTLs is in linux/drivers/char/vt.c. Other interesting code to look at would be (all in linux/drivers/char) conmakehash.c, console.c, consolemap.c, keyboard.c, tty_io.c, vc_screen.c, vga.c. Other interesting files (also in linux/drivers/char) are console_struct.h, consolemap.h, kbd_kern.h, vt_kern.h, and uni_hash.tbl. We are going to be using the IOCTLs defined in /usr/include/linux/kd.h, and /usr/include/linux/vt.h. The function prototype for ioctl() as defined in /usr/include/sys/ioctl.h is: int ioctl(int fd, int request, ...) Technically the ioctl function prototype uses 'int d' but I think 'int fd' is a lot clearer. 'fd' is the file descriptor of the console (/dev/tty), 'request' is the IOCTL we are requesting (such as KDGETLED), and '...' is argp, the arguments we are passing to ioctl(). When getting values from ioctl() we use a pointer and when the function returns, the value will be stored in our argument. ioctl() is specified in /usr/include/sys/ioctl.h We will now briefly describe the IOCTLs, the arguments it uses, and an example on how to use it where applicable. KDGETLED: This will return the current state of the LEDs. These lights on your keyboard that are on or off when something such as Caps Lock is on. Although you can turn the LEDs on or off with KDSETLED (described next) without affecting the Caps Lock, Numeric Lock, or Scroll Lock. It places one of the following values (or a combination of them) into a pointer, that points to a long int: Defined in: /usr/include/linux/kd.h 0x1 - LED_SCR, set when the Scroll Lock LED is on 0x2 - LED_NUM, set when the Numeric Lock LED is on 0x4 - LED_CAP, set when the Caps Lock LED is on As I previously mentioned, it can return combinations (or the sum) of the LEDs turned on. For example, if the Numeric Lock and Caps Lock are both on, it will return 0x6 (0x2 + 0x4). So when no lights are on, it will return 0x0. This is also how you turn all the LEDs off as described next in KDSETLED. Example: #include #include #include #include /* Keyboard IOCTLs */ #include /* ioctl() */ #include #define ERROR -1 void checkleds(); void main() { int fd; /* Console fd (/dev/tty). Used as fd in ioctl() */ long int arg; /* Where the LED states will be put into. */ /* To use as the fd in ioctl(). */ if ((fd = open("/dev/tty", O_NOCTTY)) == ERROR) { perror("open"); exit(ERROR); } /* Value stored in arg. */ if (ioctl(fd, KDGETLED, &arg) == ERROR) { perror("ioctl"); close(fd); exit(ERROR); } /* Here we print out current LEDS. */ checkleds(); } void checkleds() { /* LED_SCR = 0x1, LED_NUM = 0x2, LED_CAP = 0x4 */ if (arg == LED_SCR) printf("Scroll Lock LED is on.\n"); else if (arg == LED_NUM) printf("Numeric Lock LED is on.\n"); else if (arg == LED_CAP) printf("Caps Lock LED is on.\n"); else if (arg == LED_NUM + LED_CAP) printf("Numeric Lock and Caps Lock LEDs are on.\n"); else if (arg == LED_NUM + LED_SCR) printf("Numeric Lock and Scroll Lock LEDs are on.\n"); else if (arg == LED_CAP + LED_SCR) printf("Caps Lock and Scroll Lock LEDs are on.\n"); else if (arg == LED_NUM + LED_SCR + LED_CAP) printf("Numeric Lock, Scroll Lock, and Caps Lock LEDs are on.\n"); } KDSETLED: I will not go over the values that can be passed again (LED_CAP, LED_NUM, LED_SCR, etc.) again because I described them for KDGETLED. The argument to ioctl() is a long int (I don't see why this is a long int considering it uses a value no higher than 7) with the number of the LED you want to set. If you want to set more than one LED, add the LED #'s you want set together (such as LED_NUM + LED_CAP). This DOES NOT turn, for example, Caps Lock on. This just turns the LED on the keyboard. To actually turn Caps Lock on use KDSKBLED. Example: #include #include #include #include #include #include #include #include #define ERROR -1 int fd; /* File descriptor for console (/dev/tty/) */ void sighandler(int signum); void main() { int i; /* To be used as the fd in ioctl(). */ if ((fd = open("/dev/console", O_NOCTTY)) == ERROR) { perror("open"); exit(ERROR); } signal(SIGINT, sighandler); signal(SIGTERM, sighandler); signal(SIGQUIT, sighandler); signal(SIGTSTP, sighandler); printf("w00w00!\n\n"); printf("To exit hit Control-C.\n"); while (1) { for (i = 0x01; i <= 0x04; i++) { /* We do this because there is no LED for 0x03. */ if (i == 0x03) continue; usleep(50000); if ((ioctl(fd, KDSETLED, i)) == ERROR) { perror("ioctl"); close(fd); exit(ERROR); } } } close(fd); } void sighandler(int signum) { /* Turn off all leds. No LED == 0x0. */ if ((ioctl(fd, KDSETLED, 0x0)) == ERROR) { perror("ioctl"); close(fd); exit(ERROR); } printf("\nw00w00!\n"); close(fd); exit(0); } KDGKBLED: This is exactly the same as KDGETLED, except rather than getting the state of the LEDs, it gets the state of the flags themselves. Meaning, whether the LED is on or off doesn't matter, this returns the actual flags (if Caps Lock is on.. even if we turned off the LED with ioctl() and a KDSETLED request. The example in KDGETLED will work exactly the same except that this is getting the flags, rather than the states of the LEDs. KDSKBLED: This is exactly the same as KDSETLED except it sets the flags rather than the states of the LEDs. In other words, we can turn the Caps Lock LED off and use this which will turn Caps Lock on. Now everything will be in uppercase even though the LED light for Caps Lock isn't on. Example: This will turn off the Caps Lock LED and turn Caps Lock on. The reason we are turning the LED off is just to prove a point that just because the LED isn't on doesn't mean the flag isn't. This should show you how KDSKBLED differs from KDSETLED. This DOES NOT set the keyboard LED for Caps Lock on, this actually sets Caps Lock on. To set the LED as well use KDSETLED. #include #include #include #include #include #include #include #include #define ERROR -1 void main() { int fd; int flag = 0x4; /* To be used as the fd in ioctl(). */ if ((fd = open("/dev/console", O_NOCTTY)) == ERROR) { perror("open"); exit(ERROR); } printf("w00w00!\n\n"); /* Turn off the Caps Lock LED just to show the difference */ /* between KDSETLED and KDSKBLED. */ if ((ioctl(fd, KDSETLED, 0x0)) == ERROR) { perror("ioctl"); close(fd); exit(ERROR); } /* This turns Caps Lock itself, not the light. */ if ((ioctl(fd, KDSKBLED, flag)) == ERROR) { perror("ioctl"); close(fd); exit(ERROR); } printf("To show that Caps Lock is on even though the Caps" "Lock LED isn't...\n"); printf("Enter something: "); scanf("%s", NULL); /* Turn off any LEDs on (such as Numeric Lock) since we are */ /* turning them all off with KDSKBLED right after this. */ if ((ioctl(fd, KDSETLED, 0x0)) == ERROR) { perror("ioctl"); close(fd); exit(ERROR); } /* Now the Caps Lock is off. */ if ((ioctl(fd, KDSKBLED, 0x0)) == ERROR) { perror("ioctl"); close(fd); exit(ERROR); } close(fd); } KDGKBTYPE: This will always return KB_101 (0x02), which is the standard keyboard. So I am not even going to explain this. Two other values defined are KB_84 (0x01) and KB_OTHER (0x03). Defined in: /usr/include/linux/kd.h 0x1 - KB_84 0x2 - KB_101 (Standard/Default) 0x3 - KB_OTHER KDGETMODE: This returns the current console mode into the argument passed to ioctl() that is a pointer to a long int. The console is by default in text mode. The two modes you have are graphics and text mode. Defined in: /usr/include/linux/kd.h 0x00 - KD_TEXT - Text Mode (Default) 0x01 - KD_GRAPHICS - Graphics Mode There had been KD_TEXT0 and KD_TEXT1 but they are obsolete now. Example: #include #include #include #include #include #include #include #include #define ERROR -1 void main() { int fd; long int arg; /* To be used as the fd in ioctl(). */ if ((fd = open("/dev/console", O_NOCTTY)) == ERROR) { perror("open"); exit(ERROR); } printf("w00w00!\n\n"); if ((ioctl(fd, KDGETMODE, &arg)) == ERROR) { perror("ioctl"); close(fd); exit(ERROR); } if (arg == KD_TEXT) printf("console in text mode.\n"); else if (arg == KD_GRAPHICS) printf("console is in graphics mode.\n"); close(fd); } KDSETMODE: This takes a long int that is either set to I assume that SVGAlib uses this IOCTL (even if another function calls it). The modes and definitions are specified above. Example: ***** TEST IN GRAPHICS MODE ***** ***** TEST IN GRAPHICS MODE ***** ***** TEST IN GRAPHICS MODE ***** (test with code above) KDMKTONE: This will generate a tone, and the amount of clock cycles are passed as the argument to ioctl(). Note this is different from KIOCSOUND which is the sound generation. Example: #include #include #include #include #include #include #include #include #define ERROR -1 void main() { int i; int fd; int beep = (125 << 16) + 1591; /* This is the default beep. */ /* To be used as the fd in ioctl(). */ if ((fd = open("/dev/console", O_NOCTTY)) == ERROR) { perror("open"); exit(ERROR); } printf("w00w00!\n\n"); for (i = 0; i <= 25; i++) { if ((ioctl(fd, KDMKTONE, beep)) == ERROR) { perror("ioctl"); close(fd); exit(ERROR); } beep += 50; usleep(150000); } /* Turn off the tone now. */ if ((ioctl(fd, KDMKTONE, 0)) == ERROR) { perror("ioctl"); close(fd); exit(ERROR); } close(fd); } KIOCSOUND: This and KDMKTONE both make sounds but they are in fact different. This has tones from 0-30000 (the sounds are 1193180/frequency), whereas KDMKTONE has higher values such as, the default beep you hear when you get Ctrl-G (the value is 125<<16 + 0x637), is 8,193,591 clock cycles. Example: Note, this flashes the LEDs as well for effect, they aren't needed here. #include #include #include #include #include #include #include #include #define ERROR -1 int fd; void sighandler(int signum); void main() { int lednum; int i, j, k; /* To be used as the fd in ioctl(). */ if ((fd = open("/dev/console", O_NOCTTY)) == ERROR) { perror("open"); exit(ERROR); } printf("w00w00!.\n\n"); /* We don't want an LED still on after we quit. */ signal(SIGINT, sighandler); signal(SIGTERM, sighandler); signal(SIGQUIT, sighandler); signal(SIGTSTP, sighandler); printf("For the best sounding one, use the example values shown.\n"); printf("To exit hit Control-C.\n"); while (1) { printf("Enter range of tones to play (i.e. 100-3000): "); scanf("%d%*c%d", &i, &j); printf("Enter intervals to skip (i.e. 10): "); scanf("%d", &k); printf("Doing %d through %d, with an interval of %d:\n\n", i, j, k); for (; i <= j; i += k) { for (lednum = 0x01; lednum <= 0x04; lednum++) { if (lednum == 0x03) continue; usleep(10000); if ((ioctl(fd, KDSETLED, lednum)) == ERROR) { perror("ioctl"); close(fd); exit(ERROR); } } if ((ioctl(fd, KIOCSOUND, i)) == ERROR) { perror("ioctl"); close(fd); exit(ERROR); } printf("%d\n", i); if (i == j) break; usleep(70000); } /* Turn off all sound. */ if ((ioctl(fd, KIOCSOUND, 0x0)) == ERROR) { perror("ioctl"); close(fd); exit(ERROR); } /* Turn off all leds. */ if ((ioctl(fd, KDSETLED, 0x0)) == ERROR) { perror("ioctl"); close(fd); exit(ERROR); } putchar('\n'); } close(fd); /* Will never get this far. */ } void sighandler(int signum) { /* Stop all sound. */ if ((ioctl(fd, KIOCSOUND, 0x0)) == ERROR) { perror("ioctl"); close(fd); exit(ERROR); } /* Turn off all leds. */ if ((ioctl(fd, KDSETLED, 0x0)) == ERROR) { perror("ioctl"); close(fd); exit(ERROR); } printf("\nw00w00!\n"); close(fd); exit(0); } GIO_FONT and GIO_FONTX: These are both very similar except GIO_FONTX returns more. GIO_FONTX returns a struct consolefontdesc, and GIO_FONT only returns the font data in expanded form (which is the same as consolefontdesc's chardata). The structure for consolefontdesc is: struct consolefontdesc { unsigned short charcount; /* characters in font (256 or 512) */ unsigned short charheight; /* scan lines per character (1-32) */ char *chardata; /* font data in expanded form (8192 bytes) */ } Example: #include #include #include #include #include #include #include #include #define ERROR -1 void main() { int fd; struct consolefontdesc confont; if ((confont.chardata = malloc(8192)) == NULL) { perror("malloc"); exit(ERROR); } /* To be used as the fd in ioctl(). */ if ((fd = open("/dev/console", O_NOCTTY)) == ERROR) { perror("open"); exit(ERROR); } printf("w00w00!\n\n"); /* GIO_FONTX returns struct consolefontdesc, GIO_FONT just */ /* returns the equivalent to consolefontdesc->chardata. */ if ((ioctl(fd, GIO_FONTX, &confont)) == ERROR) { perror("ioctl"); close(fd); exit(ERROR); } printf("Character count: %u\n", confont.charcount); printf("Character height (scan lines per character): %u\n", confont.charheight); printf("Font data in expanded form: %s\n", confont.chardata); close(fd); } PIO_FONTRESET: This resets the font to the default font. You would use this if you were playing with fonts (with PIO_FONT) and messed it up, you could then reset it with this. This is no longer implemented, and I just wrote something for this for historical purposes. Example: #include #include #include #include #include #include #include #define ERROR -1 void main() { int fd; /* To be used as the fd in ioctl(). */ if ((fd = open("/dev/console", O_NOCTTY)) == ERROR) { perror("open"); exit(ERROR); } printf("w00w00!\n\n"); printf("(now calling ioctl().. this will probably fail:\n"); /* Reset the font to the default. */ if (ioctl(fd, PIO_FONTRESET, 1) == ERROR) { perror("ioctl"); close(fd); exit(ERROR); } printf("The font is now reset.\n"); close(fd); } KDGKBMODE: This will get the current keyboard mode (raw mode, unicode, half raw, etc.). The argument passed to ioctl() is a pointer to a long int. Possible modes are: raw, medium raw, Unicode, and xlate. Defined in: /usr/include/linux/kd.h 0x00 - K_RAW 0x01 - K_XLATE (the default keyboard mode) 0x02 - K_MEDIUMRAW 0x03 - K_UNICODE Example: #include #include #include #include #include #include #include #define ERROR -1 void main() { int fd; long mode; /* To be used as the fd in ioctl(). */ if ((fd = open("/dev/console", O_NOCTTY)) == ERROR) { perror("open"); exit(ERROR); } printf("w00w00!\n\n"); if ((ioctl(fd, KDGKBMODE, &mode)) == ERROR) { perror("ioctl"); close(fd); exit(ERROR); } printf("Keyboard in "); if (mode == K_RAW) printf("raw mode.\n"); else if (mode == K_XLATE) printf("xlate mode.\n"); else if (mode == K_MEDIUMRAW) printf("medium raw mode.\n"); else if (mode == K_UNICODE) printf("Unicode mode.\n"); else printf("0x0%x\n", mode); /* For future modes.\n"); close(fd); } KDSKBMODE: This will get the current keyboard mode (raw mode, unicode, half raw, etc.). The argument passed to ioctl() is a long int. I stated the possible values in KDGKBMODE. Example: #include #include #include #include #include #include #include #define ERROR -1 void main(int argc, char **argv) { int fd; long mode; if (argc < 2) { printf("Usage: %s \n\n", argv[0]); printf("Possible modes are:\n"); printf("0x00 for raw mode\n"); printf("0x01 for xlate mode (the default keyboard mode)\n"); printf("0x02 for medium raw mode\n"); printf("0x03 for Unicode mode\n\n"); exit(1); } /* To be used as the fd in ioctl(). */ if ((fd = open("/dev/console", O_NOCTTY)) == ERROR) { perror("open"); exit(ERROR); } printf("w00w00!\n\n"); mode = atoi(argv[1]); if ((ioctl(fd, KDSKBMODE, mode)) == ERROR) { perror("ioctl"); close(fd); exit(ERROR); } printf("Setting keyboard to "); if (mode == K_RAW) printf("raw mode.\n"); else if (mode == K_XLATE) printf("xlate mode.\n"); else if (mode == K_MEDIUMRAW) printf("medium raw mode.\n"); else if (mode == K_UNICODE) printf("Unicode mode.\n"); else printf("0x0%x\n", mode); /* For future modes.\n"); close(fd); } KDGKBMETA: This will get the meta key handling mode. The default meta key handling mode is the escape prefix. Defined in: /usr/include/linux/kd.h 0x00 There is no meta key handling mode set. 0x03 K_METABIT (which is setting the high order bit) 0x04 K_ESCPREFIX (escape prefix) I believe that 0x01 is also escape prefix, but I'm not sure. I get 0x01 returned from KDGKBMETA but it is not defined in /usr/include/linux/kd.h, and 0x01 is not returned in the switch statement linux/drivers/char/vt.c. Example: #include #include #include #include #include #include #include #define NONE 0 #define ERROR -1 void main() { int fd; long mode; /* To be used as the fd in ioctl(). */ if ((fd = open("/dev/console", O_NOCTTY)) == ERROR) { perror("open"); exit(ERROR); } printf("w00w00!\n\n"); if ((ioctl(fd, KDGKBMETA, &mode)) == ERROR) { perror("ioctl"); close(fd); exit(ERROR); } printf("Meta key handler is "); if (mode == NONE) printf("not set.\n"); else if (mode == K_METABIT) printf("setting high order bit.\n"); else if (mode == K_ESCPREFIX) printf("the escape prefix.\n"); else printf("0x0%x\n", mode); /* For future modes.\n"); putchar('\n'); close(fd); } KDSKBMETA: This will set the meta key handling mode. The modes are the same as the ones mentioned right before this in KDGKBMETA. Example: #include #include #include #include #include #include #include #define NONE 0 #define ERROR -1 void main(int argc, char **argv) { int fd; long mode; if (argc < 2) { printf("Usage: %s \n\n"); printf("Mode is one of the following values:\n"); printf("0x03 - K_METABIT (set high order bit)\n"); printf("0x03 - K_ESCPREFIX (escape prefix)\n\n"); } /* To be used as the fd in ioctl(). */ if ((fd = open("/dev/console", O_NOCTTY)) == ERROR) { perror("open"); exit(ERROR); } printf("w00w00!\n\n"); printf("Setting meta key handler mode "); mode = atoi(argv[1]); if (mode == NONE) printf("off.\n"); else if (mode == K_METABIT) printf("to: set high order bit.\n"); else if (mode == K_ESCPREFIX) printf("to: escape prefix.\n"); else printf("0x0%x\n", mode); /* For future values */ if ((ioctl(fd, KDSKBMETA, mode)) == ERROR) { perror("ioctl"); close(fd); exit(ERROR); } putchar('\n'); close(fd); } KDGKBENT: Gets an entry from the key translation table. It converts the keycode to an action code). You pass a struct kbentry. Defined in: /usr/include/linux/kd.h struct kbentry { u_char kb_table; /* Table number where the key map is. */ u_char kb_index; /* An index into the key map. */ u_short kb_value; /* Value returned in here. */ } It uses key_maps[] defined in linux/drivers/char/defkeymap.c, which is are pointers to other keymaps (such as plain_map[], shift_map[], etc.) All the keycodes are u_short. All the keys are greater than or equal to 0. Here are the maximum values (defined in linux/drivers/char/defkeymap.c): kb_index <= NR_KEYS (NR_KEYS == 128) kb_table <= MAX_NR_KEYMAPS (MAX_NR_KEYMAPS == 256) Example: #include #include #include #include #include #include #include #include #define ERROR -1 #define NR_KEYS 128 #define MAX_NR_KEYMAPS 256 void main() { int fd; register int i, j; struct kbentry kbent; /* To be used as the fd in ioctl(). */ if ((fd = open("/dev/console", O_NOCTTY)) == ERROR) { perror("open"); exit(ERROR); } printf("w00w00!\n\n"); kbent.kb_table = 0; /* Keyboard maps at table element 0 */ kbent.kb_index = 0; /* Index into keyboard map */ for (i = 0; i <= MAX_NR_KEYMAPS; i++) { for (j = 0; j <= NR_KEYS; j++) { if (ioctl(fd, KDGKBENT, &kbent) == ERROR) { if (errno == EINVAL) continue; else { perror("ioctl"); close(fd); exit(ERROR); } } usleep(20000); /* To slow output. */ /* Skip over no such, or in an invalid map. */ if ((kbent.kb_value == K_HOLE) || (kbent.kb_value == K_NOSUCHMAP)) continue; if (!isprint(kbent.kb_value)) { printf("Skipped unprintable character at: " "Table %d, Index %d\n", kbent.kb_table, kbent.kb_index); kbent.kb_index++; continue; } printf("Table: %d, Index: %d, ", kbent.kb_table, kbent.kb_index); kbent.kb_index++; printf("Key: %c (0x%x)\n", kbent.kb_value, kbent.kb_value); } j = 0; kbent.kb_index = j; kbent.kb_table = i; } putchar('\n'); close(fd); } KDSKBENT: Sets an entry in the key translation table. You pass a struct kbentry. See the KDGKBENT above for the structure (defined in /usr/include/linux/kd.h). I am not going to include one for this.. it would have bad results. You set the kb_table to a value as an index to the keymap, kb_index as an index into the keymap, and kb_value as the value you want to set it to. You do not pass this as a pointer (i.e. not as &kbent as seen above. KDGKBDIACR: This will print all the accented symbols (or characters). The argument passed to ioctl() is a pointer to a struct kbdiacrs. Defined in: /usr/include/linux/kd.h struct kbdiacrs { unsigned int kb_cnt; struct kbdiacr[256]; }; The structure kbdiacr is where the actual symbols and values are in (also defined in /usr/include/linux/kd.h: struct kbdiacr { u_char diacr; u_char base; u_char result; } Example: #include #include #include #include #include #include #include #define ERROR -1 void main() { int fd; register int i; struct kbdiacr kbdiacr; struct kbdiacrs kbdiacrs; /* To be used as the fd in ioctl(). */ if ((fd = open("/dev/console", O_NOCTTY)) == ERROR) { perror("open"); exit(ERROR); } printf("w00w00!\n\n"); if (ioctl(fd, KDGKBDIACR, &kbdiacrs) == ERROR) { perror("ioctl"); close(fd); exit(ERROR); } printf("Number of entries in kbdiacrs: %d\n", kbdiacrs.kb_cnt); for (i = 0; i <= kbdiacrs.kb_cnt; i++) { kbdiacr = kbdiacrs.kbdiacr[i]; printf("diacr - Character: %c, Hex: 0x%x\n", kbdiacr.diacr, kbdiacr.diacr); printf("base - Character: %c, Hex: 0x%x\n", kbdiacr.base, kbdiacr.base); printf("result - Char: %c, Hex: 0x%x\n\n", kbdiacr.result, kbdiacr.result); } putchar('\n'); close(fd); } KDGETKEYCODE: This one should be fairly obvious. It reads the keys from the kernel keycode entry. This converts the scan code to keycode. The argument to ioctl() is a struct kbkeycode. Defined in: /usr/include/linux/kd.h struct kbkeycode { unsigned int scancode; unsigned int keycode; } Example: #include #include #include #include #include #include #include #define ERROR -1 void main() { int fd; register int i; struct kbkeycode keycode; if ((fd = open("/dev/console", O_NOCTTY)) == ERROR) { perror("open"); exit(ERROR); } printf("w00w00!\n\n"); keycode.scancode = 1; /* Scancode to convert to keycode. */ while(1) { if (keycode.scancode > 255) { putchar('\n'); close(fd); exit(0); } if (ioctl(fd, KDGETKEYCODE, &keycode) == ERROR) { keycode.scancode++; continue; } printf("Keycode: %d (from scan code %d)\n", keycode.keycode, keycode.scancode); keycode.scancode++; } } KDSIGACCEPT: This is basically the equivalent of signal(). I assume signal() calls this, as this is the way to accept signals from keyboard input. The argument passed to ioctl() is the signal number the process is willing to accept. The value is greater than 1, but less than N_SIG. Example: This will not work, sorry. But included as an example anyway. If anyone knows why this will not work, please let me know. I saw no indications as to why from following the signals in linux/kernel/exit.c, but it is possible I missed something. #include #include #include #include #include #include #include #include #define ERROR -1 void main() { int fd; int signum = 2; /* Signal to accept keyboard input for. */ /* SIGINT == 2 */ if ((fd = open("/dev/console", O_NOCTTY)) == ERROR) { perror("open"); exit(ERROR); } printf("w00w00!\n\n"); printf("Ignoring SIGINT (Ctrl-C) before call to ioctl().\n"); signal(SIGINT, SIG_IGN); if (ioctl(fd, KDSIGACCEPT, signum) == ERROR) { perror("ioctl"); close(fd); exit(ERROR); } printf("Signal is now being accepted. " "Try aborting with Ctrl-C (this does not work). Hit enter.\n"); getchar(); printf("Well, you were supposed to abort rather than hit Enter.\n"); close(fd); } Now that this is the first VT_* IOCTL, I want to mention we now include /usr/include/linux/vt.h, and not /usr/include/linux/kd.h anymore. I must also mention that the next two sets of IOCTLs are my favorite. VT_* and TIOCLINUX are fun IOCTLs and can be incredibly useful and powerful. VT_OPENQRY: This will return an int that is the first available console number. If tty1-tty6 were all opened consoles, it would return 7. This value will be greater than 1, but less than the maximum number of consoles (MAX_NR_CONSOLES), is currently 63 (in Linux 2.0.33). Example: #include #include #include #include #include #include #include #define ERROR -1 void main() { int fd; int freecon; if ((fd = open("/dev/console", O_NOCTTY)) == ERROR) { perror("open"); exit(ERROR); } printf("w00w00!\n\n"); if (ioctl(fd, VT_OPENQRY, &freecon) == ERROR) { perror("ioctl"); close(fd); exit(ERROR); } printf("The first free (non opened) console is tty%d\n", freecon); close(fd); } VT_GETSTATE: This gets the state of the active VT. The argument passed to ioctl() is apointer to a struct vt_stat. Defined in: /usr/include/linux/vt.h struct vt_stat { unsigned short v_active; /* active VT */ unsigned short v_signal; /* signal to send */ unsigned short v_state; /* vt bitmask */ } Example #include #include #include #include #include #include #include #define ERROR -1 void main() { int fd; struct vt_stat stat; printf("w00w00!\n\n"); /* used as fd for ioctl() */ if ((fd = open("/dev/tty", O_NOCTTY)) == ERROR) { perror("open"); exit(ERROR); } if (ioctl(fd, VT_GETSTATE, &stat) == ERROR) { perror("ioctl"); close(fd); exit(ERROR); } printf("Current VT: tty%d\n", stat.v_active); printf("Signal to send: %d\n", stat.v_signal); printf("VT bitmask: %d\n", stat.v_state); close(fd); } VT_GETMODE: This will return the mode of the active VT. The argument to ioctl() is a pointer to a struct vt_mode. Defined in: /usr/include/linux/vt.h struct vt_mode { char mode; /* vt mode */ char waitv; /* if set, hangs on write when not active */ short relsig; /* sig to raise on release request */ short acqsig; /* sig to raise on acquisition */ short frsig; /* unused (set to 0) */ } Example: #include #include #include #include #include #include #include #define ERROR -1 void main() { int fd; struct vt_mode mode; if ((fd = open("/dev/console", O_NOCTTY)) == ERROR) { perror("open"); exit(ERROR); } printf("w00w00!\n\n"); if (ioctl(fd, VT_GETMODE, &mode) == ERROR) { perror("ioctl"); close(fd); exit(ERROR); } printf("Modes of active VT:\n"); if (mode.mode == VT_AUTO) printf("Automatic VT switching\n"); else if (mode.mode == VT_PROCESS) printf("Process controls switching.\n"); else if (mode.mode == VT_ACKACQ) printf("Acknowledgement of switches.\n"); else printf("mode == %d\n", mode.mode); if (mode.waitv) printf("Will wait on writes if not active.\n"); else printf("Will not wait on writes even when not active.\n"); printf("Signal number to release on release request: %d\n", mode.relsig); printf("Signal number to raise on acquisition: %d\n", mode.acqsig); close(fd); } VT_SETMODE: This will set the mode of the active VT. The argument to ioctl() is a struct vt_mode. In the current kernel (which is 2.0.33) you can not set the mode to VT_ACKACQ because it will return an invalid argument. I listed vt_mode structure right above in VT_GETMODE. If you turn VT_PROCESS on and then change VTs yourself, it will turn VT_PROCESS off, effectively putting it in VT_AUTO mode again. VT_RELDISP: This will release a display. The argument you pass to ioctl() is a int to the VT number (ttyX). You will call VT_DISALLOCATE after this. This will return EINVAL if the tty mode is not VT_PROCESS (which is where the process controls the switching). VT_DISALLOCATE: This deallocates the memory associated with VT. The argument passed to ioctl() is the VT to disallocate. This would be called after VT_RELDISP. Example: #include #include #include #include #include #include #include #define ERROR -1 void main() { int fd; int tty; printf("w00w00!\n\n"); if (argc < 2) { printf("Usage: %s \n", argv[0]); printf("tty is a tty to disallocate\n\n"); exit(ERROR); } tty = atoi(argv[1]); /* used as fd for ioctl() */ if ((fd = open("/dev/tty", O_NOCTTY)) == ERROR) { perror("open"); exit(ERROR); } printf("disallocating tty%d\n", tty); if (ioctl(fd, VT_DISALLOCATE, tty) == ERROR) { perror("ioctl"); close(fd); exit(ERROR); } close(fd); } VT_RESIZE: Sets the size the kernel thinks the screensize is. The argument passed to ioctl() is a pointer a struct vt_sizes. Defined in: /usr/include/linux/vt.h struct vt_sizes { ushort v_rows; /* Number of rows on screen. */ ushort v_cols; /* Number of columns on screen. */ ushort v_scrollsize; /* Not longer used. */ } This does NOT change the video mode, only what the kernel's idea of the screensize. Example: #include #include #include #include #include #include #include #define ERROR -1 void main(int argc, char **argv) { int fd; struct vt_sizes sizes; printf("w00w00!\n\n"); if (argc < 3) { printf("Usage: %s \n", argv[0]); printf("rows is the number of rows in the console\n"); printf("cols is the number of columns in the console\n\n"); exit(1); } if ((fd = open("/dev/console", O_NOCTTY)) == ERROR) { perror("open"); exit(ERROR); } sizes.v_rows = atoi(argv[1]); sizes.v_cols = atoi(argv[2]); if (ioctl(fd, VT_RESIZE, &sizes) == ERROR) { perror("ioctl"); close(fd); exit(ERROR); } printf("Kernel's idea of screensize is now:\n"); printf("Rows: %d, Columns: %d\n", sizes.v_rows, sizes.v_cols); close(fd); } VT_RESIZEX: Sets the size the kernel thinks the screen size plus more. Notice this uses a different structure than VT_RESIZE. This includes a little bit more than VT_RESIZE does. The argument passed to ioctl() is a pointer a struct vt_consize. Defined in: /usr/include/linux/vt.h struct consize { unsigned short v_rows; /* rows */ unsigned short v_cols; /* columns */ unsigned short v_vlin; /* video scan lines */ unsigned short v_clin; /* video font heigh (must be < 32) */ unsigned short v_vcol; /* video column number */ unsigned short v_ccol; /* video font width? */ } I'm not sure what v_ccol is for. Example: VT_ACTIVATE: This is what happens when you do a Alt-Fx, and it switches the VT. This can be incredible useful to be able to have a program change the VTs without having to do it manually. Example: /* ACS - Auto Console Switching */ /* What this does is allows you to specify VTs (virtual terminals, */ /* also known as consoles) to watch. When it detects new data on */ /* this tty it will automatically switch. */ /* */ /* This allows you to do many things at once such as: */ /* 1.) IRC on one VT */ /* 2.) IRC on a different IRC network on another */ /* 3.) wait for a web page to load up on another */ /* 4.) wait for telnet to connect to a site on another */ /* */ /* Compile with -DBEEPONLY to cause a beep on new activity, rather */ /* than switching to the console with new activity. */ /* */ /* So you can see the uses of this. Enjoy. */ /* Shok (Matt Conover), shok@dataforce.net */ #include #include #include #include #include #include #include #define ERROR -1 #define MAXVTNUM 63 struct vcs { int fd; char curbuf[6666], prevbuf[6666]; } vcs[MAXVTNUM]; int fd; void clean(int val); void syntax(char **argv); void parse(int argc, char **argv); void sighandler(int signum); void main(int argc, char **argv) { char buf[512]; register int i; register int tty; /* Initialization. */ bzero(buf, sizeof(buf)); for (i = 0 ; i < MAXVTNUM; i++) { vcs[i].fd = -1, vcs[i].curbuf[0] = 0; bzero(vcs[i].curbuf, sizeof(vcs[i].curbuf)); bzero(vcs[i].prevbuf, sizeof(vcs[i].prevbuf)); } /* --------------- */ printf("w00w00!\n"); parse(argc, argv); /* Take care of arguments (such as the VTs), */ /* as well as open the files. */ /* Turn into a daemon, exit the parent. */ if (fork() != 0) exit(0); /* Need to switch to the active console. tty is what we pass */ /* to ioctl() with VT_ACTIVATE. */ if ((tty = open("/dev/tty", O_RDWR)) == ERROR) { perror("open"); exit(ERROR); } /* Used so that when we receive a signal to abort, we close up */ /* all the open file descriptors, and give them a message that */ /* we are aborting. If this is run as a daemon, then you would */ /* have to send it with kill -2, -3 or -15. */ signal(SIGINT, sighandler); signal(SIGTERM, sighandler); signal(SIGQUIT, sighandler); while (1) for (i = 0; i < MAXVTNUM; i++) if (vcs[i].fd != -1) { /* Copy the current buffer into the previous one, */ /* for later use. */ strcpy(vcs[i].prevbuf, vcs[i].curbuf); /* Get to the beginning of the screen. */ lseek(vcs[i].fd, 0, SEEK_SET); if ((read(vcs[i].fd, vcs[i].curbuf, sizeof(vcs[i].curbuf))) == ERROR) { perror("read"); clean(ERROR); } /* Compare the buffer of the previous screen dumb with */ /* this one. If they are different, new data has been */ /* received and we switch consoles. */ if ((strcmp(vcs[i].curbuf, vcs[i].prevbuf) != 0) && (vcs[i].prevbuf[0] != 0)) # ifdef BEEPONLY ioctl(tty, VT_ACTIVATE, i+1); # else write(tty, '\a', 1); # endif usleep(500000); } } void parse(int argc, char **argv) { int i; char *p; char buf[512], bfa[512]; if (argc < 2) syntax(argv); sprintf(buf,"Watching "); for (argc--; argc; argc--) { if (strcasecmp(argv[argc], "all") == 0) { for (i = 0; i < MAXVTNUM; i++) { sprintf(buf, "/dev/vcs%d", i+1); vcs[i].fd = open(buf, O_RDONLY | O_NOCTTY); if (vcs[i].fd == ERROR) { perror("open"); clean(ERROR); } } printf("Watching all tty's...\n"); return; } if (strncasecmp(argv[argc], "tty", 3) != 0) syntax(argv); strcat(buf, argv[argc]); strcat(buf, " "); p = (argv[argc]+3); sprintf(bfa, "/dev/vcs%d", atoi(p)); vcs[atoi(p) - 1].fd = i = open(bfa, O_RDONLY | O_NOCTTY); if (vcs[atoi(p) - 1].fd == ERROR) { perror("open"); clean(ERROR); } } buf[strlen(buf) - 1] = 0; strcat(buf, "...\n"); printf(buf); } void syntax(char **argv) { printf("Syntax: %s \n", argv[0]); exit(ERROR); } void clean(int val) { register int i; for (i = 0; i < MAXVTNUM; i++) if (vcs[i].fd != -1) close(vcs[i].fd); close(fd); exit(val); } void sighandler(int signum) { char msg[] = "Received signal to abort. Now exiting.\n"; close(fd); fd = open("/dev/tty", O_NOCTTY | O_WRONLY); if (fd == ERROR) { printf(msg); clean(signum); } /* Give aborting message to current VT. */ write(fd, msg, sizeof(msg)); clean(signum); } VT_WAITACTIVE: This will sleep until you switch to the VT it is watching (or another program calls VT_SWITCH and switches, for example). When this occurs, ioctl() will return true. Example: #include #include #include #include #include #include #include #define ERROR -1 void main() { int fd; printf("w00w00!\n\n"); if ((fd = open("/dev/tty", O_NOCTTY)) == ERROR) { perror("open"); exit(ERROR); } if (ioctl(fd, VT_WAITACTIVE, 1) == ERROR) { perror("ioctl"); close(fd); exit(ERROR); } printf("tty1 has been activated\n"); close(fd); } VT_LOCKSWITCH: VT_UNLOCKSWITCH: VT_LOCKSWITCH disables switching the console, and VT_UNLOCKSWITCH disables locking and allows switching. Example: /* w00w00! */ /* CAP - Console Access Protection */ /* This provides security from phsyical access to your conoles. */ /* When you run this program, it will clear the screen and prompt */ /* for a password. After so many failed attempts it will lock the */ /* tty and not allow them to try anymore. While this program is */ /* running, they can't abort this program, and they can not switch */ /* consoles either. The only only way around this is to reboot the */ /* computer, in which case it will be obvious that someone tried to */ /* access your server's consoles. This will log the date and time */ /* the person tried to get access into your console. */ /* */ /* Compile: [g]cc -o CAP CAP.c -ltermcap */ /* If you have shadow passwords compile with -DUSESHADOW. */ /* To compile in debug (or testing) mode, compile with -DDEBUG. */ /* */ /* Shok (Matt Conover), shok@dataforce.net */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef DEBUG #define SIGSEGV 11 /* SIGSEGV == sig # 11 */ #endif #define ERROR -1 #define SAME 0 #define LOCKOUT 3 /* How long in minutes to lock out of the console. */ #define MAXFAIL 3 /* Number of times they can enter an invalid password. */ /* before being locked out for LOCKOUT minutes. */ /* Used to disable switching consoles. */ #define LOCKVT(x) if ((ioctl(fd, VT_LOCKSWITCH, 1)) == ERROR) { \ perror("locking console (/dev/tty/)"); \ exit(ERROR); \ } /* Used to reenable ability to switch consoles. */ #define UNLOCKVT(x) if ((ioctl(fd, VT_UNLOCKSWITCH, 1)) == ERROR) { \ perror("locking console (/dev/tty/)"); \ exit(ERROR); \ } int fd; /* Console fd. */ char *strip(char *str); /* Used to strip newlines from ctime(). */ #ifdef DEBUG void sighandler(int signum); #endif DEBUG void main() { int uid; int failed = 0; /* Number of failed attempts out of MAXFAIL. */ int totfailed = 0; /* Number of total failed attempts (not reseted). */ time_t tm; char curtime[64]; /* Don't change passwd or realpasswd's length. This is the maximum */ /* password length allow from getpass(). Any smaller can overflow. */ char *pass, passwd[128], realpasswd[128]; struct passwd *pwd; #ifdef USESHADOW struct spwd *spwd; #endif if ((fd = open("/dev/tty", O_RDWR)) == ERROR) { perror("opening console (/dev/tty)"); exit(ERROR); } /* Disable signals (so attackers can't abort program). */ #ifndef DEBUG signal(SIGHUP, SIG_IGN); signal(SIGINT, SIG_IGN); signal(SIGTERM, SIG_IGN); signal(SIGQUIT, SIG_IGN); signal(SIGTSTP, SIG_IGN); #else signal(SIGINT, sighandler); signal(SIGTERM, sighandler); signal(SIGQUIT, sighandler); signal(SIGTSTP, sighandler); signal(SIGSEGV, sighandler); #endif LOCKVT(fd); /* Lock the VT. It can no longer switch. */ uid = getuid(); pwd = getpwuid(uid); #ifdef USESHADOW if ((spwd = getspnam(pwd->pw_name)) == NULL) { perror("getspnam"); exit(ERROR); } strncpy(realpasswd, spwd->sp_pwdp, sizeof(realpasswd)); #else strncpy(realpasswd, pwd->pw_passwd, sizeof(realpasswd)); #endif clr(); /* clear the screen */ printf("w00w00!\n"); printf("Console is now locked.\n"); getchar(); /* Used to log invalid password attempts. */ openlog("CAP/conprot", LOG_CONS, LOG_AUTHPRIV); while (1) { /* Get the password from the user. */ pass = getpass("Enter password: "); /* Encrypt the password from getpass(). /* Note, we are using realpasswd for our salt. This is to allow a */ /* salt of any size. This also saving us the trouble of getting */ /* the salt ourselves. */ strncpy(passwd, crypt(pass, realpasswd), sizeof(passwd)); passwd[128] = '\0'; /* NULL terminate passwd just to be safe. */ #ifdef DEBUG printf("Encrypted password from user: %s\n", passwd); printf("The real encrypted password: %s\n", realpasswd); #endif if ((strcmp(passwd, realpasswd)) == SAME) { /* Unlock the console, to allow it to switch. */ UNLOCKVT(fd); closelog(); /* Close logging. */ clr(); printf("Everything is now restored.\n"); if (totfailed == 0) printf("No one tried to access the console.\n"); else printf("Total number of failed attempts to unlock console: %d\n", totfailed); exit(0); } else { failed++, totfailed++; /* Increase number of failed attempts. */ /* Log bad attempts to syslog. */ tm = time(NULL); snprintf(curtime, sizeof(curtime), (char *)ctime(&tm)); strip(curtime); /* Strip new lines out of the time. */ syslog(LOG_WARNING, "Failed access attempt on: %s", curtime); printf("Invalid password.\n"); if (failed >= MAXFAIL) { printf("Maximum number of failed attempts.\n" "Now locking for %d minutes.\n", LOCKOUT); sleep(LOCKOUT * 60); /* Convert the minutes to seconds. */ failed = 0; /* Reset the number of failed attempts. */ } } } } char *strip(char *str) { register int i; for (i = 0; str[i]; i++) /* Strip newline out of string. */ /* We do this because syslog appends the newline itself. */ if (str[i] == '\n') str[i] = '\0'; return str; } #ifdef DEBUG void sighandler(int signum) { if (signum == SIGSEGV) printf("Received SIGSEGV.\n"); printf("\nAborting and unlocking console.\n"); UNLOCKVT(fd); if (signum == 11) kill(getpid(), 11); exit(0); } #endif /* Clear the screen usning termcap */ clr() { char *clear; char clbuf[1024], *clbp = clbuf; if (tgetent(clbuf, getenv("TERM")) == ERROR) { perror("tgetent"); system("clear"); return; } if ((clear = tgetstr("cl", &clbp)) == NULL) { perror("tgetent"); system("clear"); return; } if (clear) tputs(clear, tgetnum("li"), putchar); } VT_SENDSIG: This is in the include file, but it isn't used linux/drivers/char anywhere. This is to send a signal to the bitmask of a VT. Because it isn't used anywhere I'm not going to include an example, I'm just mentioning this for your information. There are a whole series of functions with TIOCLINUX. You use a subcode (somewhat like you do with ICMP functions) to do different things. You may notice some subcodes (such as subcode 2 and 10) also take additional arguments. TIOCLINUX: We will show these subcodes in order: subcode 0: This is to dump the screen. This is obsolete now, because this is what /dev/vcsX and /dev/vcsaX now (which is what our ACS seen above uses to detect new data). Further note: subcode 0, 8, and 9 have all be replaced by /dev/vcsX and /dev/vcsaX. subcode 1: This will return task information. This is now deprecated, so avoid use of it (why would you need to use this?). subcode 2: This works with subcode 3 as well. This is for cutting and pasting. You pass it a struct that contains the subcode, the start row column, the end row and column, and the selection mode (0 for character by character, 1 for word-by-word, 2 for line-by-line cut and selecting). The characters selected (or cut) are highlighted and put into a buffer that is in the sel_buffer (in linux/drivers/char/console.c). The argument is a pointer to a struct that looks like this: struct { char subcode; /* this will be 2 */ short xs; /* where to start cutting (0 = left side of screen) */ short ys; /* where to start cutting (0 = top of the screen) */ short xe; /* where to end the cutting */ short ye; /* where to end the cutting */ } subcode 3: This works with subcode 2. subcode 2 cuts out some test from the console and this pastes it to the fd (from the sel_buffer mentioned above in subcode 2). subcode 4: This will unblank the screen (this is what happens when the screen saver comes on and then you hit a key). Example: Will be added subcode 5: This is to set the lookup table defining characters in a "word", for word-by-word selection. It is user settable table that defines the characters that are considered to be alphabetic. This will be stored in inWordLut (which is in linux/drivers/char/selection.c. It looks like this: static u32 inwordLut[8] = { 0x00000000, /* control chars */ 0x03FF0000, /* digits */ 0x87FFFFFE, /* uppercase and '_' */ 0x07FFFFFE, /* lowercase */ 0x00000000, 0x00000000, 0xFF7FFFFF, /* latin-1 accented letters, not multiplication sign */ 0xFF7FFFFF /* latin-1 accented letters, not division sign */ }; subcode 6: This will return the shift_state. The argument passed to ioctl() is a pointer to a char. subcode 7: This will return the report_mouse. The argument passed to ioctl() is a pointer to a char. subcode 8: This dumps the screen height, cursor position, and all character attribute pairs. This is obsolete now and the kernel will report an error if you try to use it. This is because we now have /dev/vcsX and /dev/vcsaX and you can just read from there. Only use this if you have a kernel between 1.1.67 and 1.1.92. subcode 9: This will restore the width and height, cursor position, and all character attributes. If you try to use this in anything later than 1.1.92 you will get an error. Write to /dev/vcsX or /dev/vcsaX instead. subcode 10: This is what handles the power saving for new generation monitors. If you are idle for so long the screen will blank. The argument passed to this will be a pointer to a structure that includes the subcode (which will be 10) and one of the following types: 0: Screen blanking is disabled. 1: Video adapter registers are saved and the monitor is put into standby mode (it turns off vertical sychronization pulses). If your monitor has an Off_Mode timer, it will eventually power down by itself. 2: The settings are saved and then it turns the monitor off (it turns off both vertical and horizontal sychronization pulses. If your monitor doesn't have an Off_Mode timer, or you want your monitor to power down immediately when the blank_timer times out, use this. We will use the following structure: struct { char subcode; /* This will be 10 */ char mode; /* 0: disable screen blanking */ /* 1: go into "standby" mode */ /* 1: go into "off" mode */ } Conclusion: I am sorry for the length of this article, but I wanted to give examples for many of these to show the uses of console IOCTLs. As I mentioned in the introduction, IOCTLs are being replaced by POSIX, but this is better. Why do you need console IOCTLs? There are a lot of undocumented things that you can do with console IOCTLs you can't do otherwise (for example, ACS and CAP). Shok (Matt Conover) Email: shok@dataforce.net Web: http://www.w00w00.org