]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | * consoles.c Routines to detect the system consoles | |
3 | * | |
4 | * Copyright (c) 2011 SuSE LINUX Products GmbH, All rights reserved. | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License as published by | |
8 | * the Free Software Foundation; either version 2, or (at your option) | |
9 | * any later version. | |
10 | * | |
11 | * This program is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | * GNU General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU General Public License | |
17 | * along with this program (see the file COPYING); if not, write to the | |
18 | * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, | |
19 | * MA 02110-1301, USA. | |
20 | * | |
21 | * Author: Werner Fink <werner@suse.de> | |
22 | */ | |
23 | ||
24 | #include <limits.h> | |
25 | #include <stdio.h> | |
26 | #include <stdlib.h> | |
27 | #include <string.h> | |
28 | #include <sys/types.h> | |
29 | #include <sys/stat.h> | |
30 | #include <sys/ioctl.h> | |
31 | #ifdef __linux__ | |
32 | # ifndef TIOCGDEV | |
33 | # include <sys/vt.h> | |
34 | # include <sys/kd.h> | |
35 | # include <linux/serial.h> | |
36 | # endif | |
37 | #endif | |
38 | #include <fcntl.h> | |
39 | #include <dirent.h> | |
40 | #include <unistd.h> | |
41 | #include "consoles.h" | |
42 | ||
43 | #ifdef __linux__ | |
44 | # include <linux/major.h> | |
45 | #endif | |
46 | ||
47 | #if !defined(__STDC_VERSION__) || (__STDC_VERSION__ < 199901L) | |
48 | # ifndef typeof | |
49 | # define typeof __typeof__ | |
50 | # endif | |
51 | # ifndef restrict | |
52 | # define restrict __restrict__ | |
53 | # endif | |
54 | #endif | |
55 | ||
56 | #define alignof(type) ((sizeof(type)+(sizeof(void*)-1)) & ~(sizeof(void*)-1)) | |
57 | ||
58 | struct console *consoles; | |
59 | ||
60 | /* | |
61 | * Read and allocate one line from file, | |
62 | * the caller has to free the result | |
63 | */ | |
64 | static | |
65 | #ifdef __GNUC__ | |
66 | __attribute__((__nonnull__)) | |
67 | #endif | |
68 | char *oneline(const char *file) | |
69 | { | |
70 | FILE *fp; | |
71 | char *ret = (char*)0, *nl; | |
72 | size_t len = 0; | |
73 | ||
74 | if ((fp = fopen(file, "re")) == (FILE*)0) | |
75 | goto err; | |
76 | if (getline(&ret, &len, fp) < 0) | |
77 | goto out; | |
78 | if (len) | |
79 | ret[len-1] = '\0'; | |
80 | if ((nl = strchr(ret, '\n'))) | |
81 | *nl = '\0'; | |
82 | out: | |
83 | fclose(fp); | |
84 | err: | |
85 | return ret; | |
86 | } | |
87 | ||
88 | #ifdef __linux__ | |
89 | /* | |
90 | * Read and determine active attribute for tty, | |
91 | * the caller has to free the result. | |
92 | */ | |
93 | static | |
94 | __attribute__((__malloc__)) | |
95 | char *actattr(const char *tty) | |
96 | { | |
97 | char *ret = (char*)0; | |
98 | char *path; | |
99 | ||
100 | if (!tty || *tty == '\0') | |
101 | goto err; | |
102 | ||
103 | if (asprintf(&path, "/sys/class/tty/%s/active", tty) < 0) | |
104 | goto err; | |
105 | ||
106 | if ((ret = oneline(path)) == (char*)0) | |
107 | goto out; | |
108 | out: | |
109 | free(path); | |
110 | err: | |
111 | return ret; | |
112 | } | |
113 | ||
114 | /* Read and determine device attribute for tty */ | |
115 | static | |
116 | dev_t devattr(const char *tty) | |
117 | { | |
118 | unsigned int maj, min; | |
119 | dev_t dev = 0; | |
120 | char *path, *value; | |
121 | ||
122 | if (!tty || *tty == '\0') | |
123 | goto err; | |
124 | ||
125 | if (asprintf(&path, "/sys/class/tty/%s/dev", tty) < 0) | |
126 | goto err; | |
127 | ||
128 | if ((value = oneline(path)) == (char*)0) | |
129 | goto out; | |
130 | ||
131 | if (sscanf(value, "%u:%u", &maj, &min) == 2) | |
132 | dev = makedev(maj, min); | |
133 | free(value); | |
134 | out: | |
135 | free(path); | |
136 | err: | |
137 | return dev; | |
138 | } | |
139 | #endif /* __linux__ */ | |
140 | ||
141 | static dev_t comparedev; | |
142 | static | |
143 | #ifdef __GNUC__ | |
144 | __attribute__((__nonnull__,__malloc__,__hot__)) | |
145 | #endif | |
146 | char* scandev(DIR *dir) | |
147 | { | |
148 | char *name = (char*)0; | |
149 | struct dirent *dent; | |
150 | int fd; | |
151 | ||
152 | fd = dirfd(dir); | |
153 | rewinddir(dir); | |
154 | while ((dent = readdir(dir))) { | |
155 | char path[PATH_MAX]; | |
156 | struct stat st; | |
157 | if (fstatat(fd, dent->d_name, &st, 0) < 0) | |
158 | continue; | |
159 | if (!S_ISCHR(st.st_mode)) | |
160 | continue; | |
161 | if (comparedev != st.st_rdev) | |
162 | continue; | |
163 | if ((size_t)snprintf(path, sizeof(path), "/dev/%s", dent->d_name) >= sizeof(path)) | |
164 | continue; | |
165 | name = realpath(path, NULL); | |
166 | break; | |
167 | } | |
168 | return name; | |
169 | } | |
170 | ||
171 | static | |
172 | struct chardata initcp = { | |
173 | CERASE, | |
174 | CKILL, | |
175 | CTRL('r'), | |
176 | 0 | |
177 | }; | |
178 | ||
179 | static int concount; /* Counter for console IDs */ | |
180 | ||
181 | static | |
182 | #ifdef __GNUC__ | |
183 | __attribute__((__nonnull__,__hot__)) | |
184 | #endif | |
185 | void consalloc(char * name) | |
186 | { | |
187 | struct console *restrict tail; | |
188 | ||
189 | if (posix_memalign((void*)&tail, sizeof(void*), alignof(typeof(struct console))) != 0) | |
190 | perror("memory allocation"); | |
191 | ||
192 | tail->next = (struct console*)0; | |
193 | tail->tty = name; | |
194 | ||
195 | tail->file = (FILE*)0; | |
196 | tail->flags = 0; | |
197 | tail->fd = -1; | |
198 | tail->id = concount++; | |
199 | tail->pid = 0; | |
200 | memset(&tail->tio, 0, sizeof(tail->tio)); | |
201 | memcpy(&tail->cp, &initcp, sizeof(struct chardata)); | |
202 | ||
203 | if (!consoles) | |
204 | consoles = tail; | |
205 | else | |
206 | consoles->next = tail; | |
207 | } | |
208 | ||
209 | void detect_consoles(const char *console, int fallback) | |
210 | { | |
211 | #ifdef __linux__ | |
212 | char *attrib, *cmdline; | |
213 | FILE *fc; | |
214 | ||
215 | if ((fc = fopen("/proc/consoles", "re"))) { | |
216 | char fbuf[16]; | |
217 | int maj, min; | |
218 | DIR *dir; | |
219 | dir = opendir("/dev"); | |
220 | if (!dir) | |
221 | goto out1; | |
222 | while ((fscanf(fc, "%*s %*s (%[^)]) %d:%d", &fbuf[0], &maj, &min) == 3)) { | |
223 | char * name; | |
224 | ||
225 | if (!strchr(fbuf, 'E')) | |
226 | continue; | |
227 | comparedev = makedev(maj, min); | |
228 | ||
229 | name = scandev(dir); | |
230 | if (!name) | |
231 | continue; | |
232 | consalloc(name); | |
233 | } | |
234 | closedir(dir); | |
235 | out1: | |
236 | fclose(fc); | |
237 | return; | |
238 | } | |
239 | ||
240 | if ((attrib = actattr("console"))) { | |
241 | char *words = attrib, *token; | |
242 | DIR *dir; | |
243 | ||
244 | dir = opendir("/dev"); | |
245 | if (!dir) | |
246 | goto out2; | |
247 | while ((token = strsep(&words, " \t\r\n"))) { | |
248 | char * name; | |
249 | ||
250 | if (*token == '\0') | |
251 | continue; | |
252 | comparedev = devattr(token); | |
253 | if (comparedev == makedev(TTY_MAJOR, 0)) { | |
254 | char *tmp = actattr(token); | |
255 | if (!tmp) | |
256 | continue; | |
257 | comparedev = devattr(tmp); | |
258 | free(tmp); | |
259 | } | |
260 | ||
261 | name = scandev(dir); | |
262 | if (!name) | |
263 | continue; | |
264 | consalloc(name); | |
265 | } | |
266 | closedir(dir); | |
267 | out2: | |
268 | free(attrib); | |
269 | return; | |
270 | ||
271 | } | |
272 | ||
273 | if ((cmdline = oneline("/proc/cmdline"))) { | |
274 | char *words= cmdline, *token; | |
275 | DIR *dir; | |
276 | ||
277 | dir = opendir("/dev"); | |
278 | if (!dir) | |
279 | goto out3; | |
280 | while ((token = strsep(&words, " \t\r\n"))) { | |
281 | #ifdef TIOCGDEV | |
282 | unsigned int devnum; | |
283 | #else | |
284 | struct vt_stat vt; | |
285 | struct stat st; | |
286 | #endif | |
287 | char *colon, *name; | |
288 | int fd; | |
289 | ||
290 | if (*token != 'c') | |
291 | continue; | |
292 | ||
293 | if (strncmp(token, "console=", 8) != 0) | |
294 | continue; | |
295 | token += 8; | |
296 | ||
297 | if (strcmp(token, "brl") == 0) | |
298 | token += 4; | |
299 | if ((colon = strchr(token, ','))) | |
300 | *colon = '\0'; | |
301 | ||
302 | if (asprintf(&name, "/dev/%s", token) < 0) | |
303 | continue; | |
304 | ||
305 | if ((fd = open(name, O_RDWR|O_NONBLOCK|O_NOCTTY|O_CLOEXEC)) < 0) { | |
306 | free(name); | |
307 | continue; | |
308 | } | |
309 | free(name); | |
310 | #ifdef TIOCGDEV | |
311 | if (ioctl (fd, TIOCGDEV, &devnum) < 0) { | |
312 | close(fd); | |
313 | continue; | |
314 | } | |
315 | comparedev = (dev_t)devnum; | |
316 | #else | |
317 | if (fstat(fd, &st) < 0) { | |
318 | close(fd); | |
319 | continue; | |
320 | } | |
321 | comparedev = st.st_rdev; | |
322 | if (comparedev == makedev(TTY_MAJOR, 0)) { | |
323 | if (ioctl(fd, VT_GETSTATE, &vt) < 0) { | |
324 | close(fd); | |
325 | continue; | |
326 | } | |
327 | comparedev = makedev(TTY_MAJOR, (int)vt.v_active); | |
328 | } | |
329 | #endif | |
330 | close(fd); | |
331 | ||
332 | name = scandev(dir); | |
333 | if (!name) | |
334 | continue; | |
335 | consalloc(name); | |
336 | } | |
337 | closedir(dir); | |
338 | out3: | |
339 | free(cmdline); | |
340 | return; | |
341 | } | |
342 | #endif /* __linux __ */ | |
343 | if (console && *console) { | |
344 | int fd; | |
345 | DIR *dir; | |
346 | char *name; | |
347 | #ifdef TIOCGDEV | |
348 | unsigned int devnum; | |
349 | #else | |
350 | # ifdef __linux__ | |
351 | struct vt_stat vt; | |
352 | # endif | |
353 | struct stat st; | |
354 | #endif | |
355 | ||
356 | if ((fd = open(console, O_RDWR|O_NONBLOCK|O_NOCTTY|O_CLOEXEC)) < 0) | |
357 | return; | |
358 | #ifdef TIOCGDEV | |
359 | if (ioctl (fd, TIOCGDEV, &devnum) < 0) { | |
360 | close(fd); | |
361 | return; | |
362 | } | |
363 | comparedev = (dev_t)devnum; | |
364 | #else | |
365 | if (fstat(fd, &st) < 0) { | |
366 | close(fd); | |
367 | return; | |
368 | } | |
369 | # ifdef __linux__ | |
370 | comparedev = st.st_rdev; | |
371 | if (comparedev == makedev(TTY_MAJOR, 0)) { | |
372 | if (ioctl(fd, VT_GETSTATE, &vt) < 0) { | |
373 | close(fd); | |
374 | return; | |
375 | } | |
376 | comparedev = makedev(TTY_MAJOR, (int)vt.v_active); | |
377 | } | |
378 | # endif | |
379 | #endif | |
380 | close(fd); | |
381 | dir = opendir("/dev"); | |
382 | if (!dir) | |
383 | return; | |
384 | name = scandev(dir); | |
385 | if (name) | |
386 | consalloc(name); | |
387 | closedir(dir); | |
388 | return; | |
389 | } | |
390 | ||
391 | if (fallback >= 0) { | |
392 | const char *name = ttyname(fallback); | |
393 | if (!name) | |
394 | name = "/dev/console"; | |
395 | consalloc(strdup(name)); | |
396 | if (consoles) | |
397 | consoles->fd = fallback; | |
398 | } | |
399 | } |