linux.c 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343
  1. /*
  2. *
  3. * Copyright © 1999 Keith Packard
  4. *
  5. * Permission to use, copy, modify, distribute, and sell this software and its
  6. * documentation for any purpose is hereby granted without fee, provided that
  7. * the above copyright notice appear in all copies and that both that
  8. * copyright notice and this permission notice appear in supporting
  9. * documentation, and that the name of Keith Packard not be used in
  10. * advertising or publicity pertaining to distribution of the software without
  11. * specific, written prior permission. Keith Packard makes no
  12. * representations about the suitability of this software for any purpose. It
  13. * is provided "as is" without express or implied warranty.
  14. *
  15. * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
  16. * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
  17. * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR
  18. * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
  19. * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
  20. * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  21. * PERFORMANCE OF THIS SOFTWARE.
  22. */
  23. #ifdef HAVE_CONFIG_H
  24. #include <kdrive-config.h>
  25. #endif
  26. #include "kdrive.h"
  27. #include <errno.h>
  28. #include <signal.h>
  29. #include <linux/vt.h>
  30. #include <linux/kd.h>
  31. #include <sys/stat.h>
  32. #include <sys/ioctl.h>
  33. #include <X11/keysym.h>
  34. #include <linux/apm_bios.h>
  35. static int vtno;
  36. int LinuxConsoleFd;
  37. static int LinuxApmFd;
  38. static int activeVT;
  39. static Bool enabled;
  40. static void LinuxVTRequest(int sig)
  41. {
  42. kdSwitchPending = TRUE;
  43. }
  44. /* Check before chowning -- this avoids touching the file system */
  45. static void LinuxCheckChown(char *file)
  46. {
  47. struct stat st;
  48. __uid_t u;
  49. __gid_t g;
  50. if (stat(file, &st) < 0)
  51. return;
  52. u = getuid();
  53. g = getgid();
  54. if (st.st_uid != u || st.st_gid != g)
  55. chown(file, u, g);
  56. }
  57. static int LinuxInit(void)
  58. {
  59. int fd = -1;
  60. LinuxApmFd = -1;
  61. char vtname[11];
  62. struct vt_stat vts;
  63. LinuxConsoleFd = -1;
  64. /* check if we're run with euid==0 */
  65. if (geteuid() != 0) {
  66. FatalError("LinuxInit: Server must be suid root\n");
  67. }
  68. if (kdVirtualTerminal >= 0)
  69. vtno = kdVirtualTerminal;
  70. else {
  71. if ((fd = open("/dev/tty0", O_WRONLY, 0)) < 0) {
  72. FatalError("LinuxInit: Cannot open /dev/tty0 (%s)\n",
  73. strerror(errno));
  74. }
  75. if ((ioctl(fd, VT_OPENQRY, &vtno) < 0) || (vtno == -1)) {
  76. FatalError("xf86OpenConsole: Cannot find a free VT\n");
  77. }
  78. }
  79. close(fd);
  80. sprintf(vtname, "/dev/tty%d", vtno); /* /dev/tty1-64 */
  81. if ((LinuxConsoleFd = open(vtname, O_RDWR | O_NDELAY, 0)) < 0) {
  82. FatalError("LinuxInit: Cannot open %s (%s)\n",
  83. vtname, strerror(errno));
  84. }
  85. /* change ownership of the vt */
  86. LinuxCheckChown(vtname);
  87. /*
  88. * the current VT device we're running on is not "console", we want
  89. * to grab all consoles too
  90. *
  91. * Why is this needed?
  92. */
  93. LinuxCheckChown("/dev/tty0");
  94. /*
  95. * Linux doesn't switch to an active vt after the last close of a vt,
  96. * so we do this ourselves by remembering which is active now.
  97. */
  98. memset(&vts, '\0', sizeof(vts)); /* valgrind */
  99. if (ioctl(LinuxConsoleFd, VT_GETSTATE, &vts) == 0) {
  100. activeVT = vts.v_active;
  101. }
  102. return 1;
  103. }
  104. static void LinuxSetSwitchMode(int mode)
  105. {
  106. struct sigaction act;
  107. struct vt_mode VT;
  108. if (ioctl(LinuxConsoleFd, VT_GETMODE, &VT) < 0) {
  109. FatalError("LinuxInit: VT_GETMODE failed\n");
  110. }
  111. if (mode == VT_PROCESS) {
  112. act.sa_handler = LinuxVTRequest;
  113. sigemptyset(&act.sa_mask);
  114. act.sa_flags = 0;
  115. sigaction(SIGUSR1, &act, 0);
  116. VT.mode = mode;
  117. VT.relsig = SIGUSR1;
  118. VT.acqsig = SIGUSR1;
  119. } else {
  120. act.sa_handler = SIG_IGN;
  121. sigemptyset(&act.sa_mask);
  122. act.sa_flags = 0;
  123. sigaction(SIGUSR1, &act, 0);
  124. VT.mode = mode;
  125. VT.relsig = 0;
  126. VT.acqsig = 0;
  127. }
  128. if (ioctl(LinuxConsoleFd, VT_SETMODE, &VT) < 0) {
  129. FatalError("LinuxInit: VT_SETMODE failed\n");
  130. }
  131. }
  132. static void
  133. LinuxApmBlock(pointer blockData, OSTimePtr pTimeout, pointer pReadmask)
  134. {
  135. }
  136. static Bool LinuxApmRunning;
  137. static void LinuxApmWakeup(pointer blockData, int result, pointer pReadmask)
  138. {
  139. fd_set *readmask = (fd_set *) pReadmask;
  140. if (result > 0 && LinuxApmFd >= 0 && FD_ISSET(LinuxApmFd, readmask)) {
  141. apm_event_t event;
  142. Bool running = LinuxApmRunning;
  143. int cmd = APM_IOC_SUSPEND;
  144. while (read(LinuxApmFd, &event, sizeof(event)) == sizeof(event)) {
  145. switch (event) {
  146. case APM_SYS_STANDBY:
  147. case APM_USER_STANDBY:
  148. running = FALSE;
  149. cmd = APM_IOC_STANDBY;
  150. break;
  151. case APM_SYS_SUSPEND:
  152. case APM_USER_SUSPEND:
  153. case APM_CRITICAL_SUSPEND:
  154. running = FALSE;
  155. cmd = APM_IOC_SUSPEND;
  156. break;
  157. case APM_NORMAL_RESUME:
  158. case APM_CRITICAL_RESUME:
  159. case APM_STANDBY_RESUME:
  160. running = TRUE;
  161. break;
  162. }
  163. }
  164. if (running && !LinuxApmRunning) {
  165. KdResume();
  166. LinuxApmRunning = TRUE;
  167. } else if (!running && LinuxApmRunning) {
  168. KdSuspend();
  169. LinuxApmRunning = FALSE;
  170. ioctl(LinuxApmFd, cmd, 0);
  171. }
  172. }
  173. }
  174. #ifdef FNONBLOCK
  175. #define NOBLOCK FNONBLOCK
  176. #else
  177. #define NOBLOCK FNDELAY
  178. #endif
  179. static void LinuxEnable(void)
  180. {
  181. if (enabled)
  182. return;
  183. if (kdSwitchPending) {
  184. kdSwitchPending = FALSE;
  185. ioctl(LinuxConsoleFd, VT_RELDISP, VT_ACKACQ);
  186. }
  187. /*
  188. * Open the APM driver
  189. */
  190. LinuxApmFd = open("/dev/apm_bios", 2);
  191. if (LinuxApmFd < 0 && errno == ENOENT)
  192. LinuxApmFd = open("/dev/misc/apm_bios", 2);
  193. if (LinuxApmFd >= 0) {
  194. LinuxApmRunning = TRUE;
  195. fcntl(LinuxApmFd, F_SETFL,
  196. fcntl(LinuxApmFd, F_GETFL) | NOBLOCK);
  197. RegisterBlockAndWakeupHandlers(LinuxApmBlock, LinuxApmWakeup,
  198. 0);
  199. AddEnabledDevice(LinuxApmFd);
  200. }
  201. /*
  202. * now get the VT
  203. */
  204. LinuxSetSwitchMode(VT_AUTO);
  205. if (ioctl(LinuxConsoleFd, VT_ACTIVATE, vtno) != 0) {
  206. FatalError("LinuxInit: VT_ACTIVATE failed\n");
  207. }
  208. if (ioctl(LinuxConsoleFd, VT_WAITACTIVE, vtno) != 0) {
  209. FatalError("LinuxInit: VT_WAITACTIVE failed\n");
  210. }
  211. LinuxSetSwitchMode(VT_PROCESS);
  212. if (ioctl(LinuxConsoleFd, KDSETMODE, KD_GRAPHICS) < 0) {
  213. FatalError("LinuxInit: KDSETMODE KD_GRAPHICS failed\n");
  214. }
  215. enabled = TRUE;
  216. }
  217. static Bool LinuxSpecialKey(KeySym sym)
  218. {
  219. struct vt_stat vts;
  220. int con;
  221. if (XK_F1 <= sym && sym <= XK_F12) {
  222. con = sym - XK_F1 + 1;
  223. memset(&vts, '\0', sizeof(vts)); /* valgrind */
  224. ioctl(LinuxConsoleFd, VT_GETSTATE, &vts);
  225. if (con != vts.v_active && (vts.v_state & (1 << con))) {
  226. ioctl(LinuxConsoleFd, VT_ACTIVATE, con);
  227. return TRUE;
  228. }
  229. }
  230. return FALSE;
  231. }
  232. static void LinuxDisable(void)
  233. {
  234. ioctl(LinuxConsoleFd, KDSETMODE, KD_TEXT); /* Back to text mode ... */
  235. if (kdSwitchPending) {
  236. kdSwitchPending = FALSE;
  237. ioctl(LinuxConsoleFd, VT_RELDISP, 1);
  238. }
  239. enabled = FALSE;
  240. if (LinuxApmFd >= 0) {
  241. RemoveBlockAndWakeupHandlers(LinuxApmBlock, LinuxApmWakeup, 0);
  242. RemoveEnabledDevice(LinuxApmFd);
  243. close(LinuxApmFd);
  244. LinuxApmFd = -1;
  245. }
  246. }
  247. static void LinuxFini(void)
  248. {
  249. struct vt_mode VT;
  250. struct vt_stat vts;
  251. int fd;
  252. if (LinuxConsoleFd < 0)
  253. return;
  254. if (ioctl(LinuxConsoleFd, VT_GETMODE, &VT) != -1) {
  255. VT.mode = VT_AUTO;
  256. ioctl(LinuxConsoleFd, VT_SETMODE, &VT); /* set dflt vt handling */
  257. }
  258. memset(&vts, '\0', sizeof(vts)); /* valgrind */
  259. ioctl(LinuxConsoleFd, VT_GETSTATE, &vts);
  260. if (vtno == vts.v_active) {
  261. /*
  262. * Find a legal VT to switch to, either the one we started from
  263. * or the lowest active one that isn't ours
  264. */
  265. if (activeVT < 0 ||
  266. activeVT == vts.v_active ||
  267. !(vts.v_state & (1 << activeVT))) {
  268. for (activeVT = 1; activeVT < 16; activeVT++)
  269. if (activeVT != vtno
  270. && (vts.v_state & (1 << activeVT)))
  271. break;
  272. if (activeVT == 16)
  273. activeVT = -1;
  274. }
  275. /*
  276. * Perform a switch back to the active VT when we were started
  277. */
  278. if (activeVT >= -1) {
  279. ioctl(LinuxConsoleFd, VT_ACTIVATE, activeVT);
  280. ioctl(LinuxConsoleFd, VT_WAITACTIVE, activeVT);
  281. activeVT = -1;
  282. }
  283. }
  284. close(LinuxConsoleFd); /* make the vt-manager happy */
  285. fd = open("/dev/tty0", O_RDWR | O_NDELAY, 0);
  286. if (fd >= 0) {
  287. memset(&vts, '\0', sizeof(vts)); /* valgrind */
  288. ioctl(fd, VT_GETSTATE, &vts);
  289. if (ioctl(fd, VT_DISALLOCATE, vtno) < 0)
  290. fprintf(stderr,
  291. "Can't deallocate console %d errno %d\n", vtno,
  292. errno);
  293. close(fd);
  294. }
  295. return;
  296. }
  297. static const KdOsFuncs LinuxFuncs = {
  298. LinuxInit,
  299. LinuxEnable,
  300. LinuxSpecialKey,
  301. LinuxDisable,
  302. LinuxFini,
  303. 0
  304. };
  305. void OsVendorInit(void)
  306. {
  307. KdOsInit(&LinuxFuncs);
  308. }