Enhance src/consoles.c and src/consoles.h to reflect latest
[sysvinit.git] / src / consoles.c
CommitLineData
32c9fc6d
DWF
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>
d212d886
DWF
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>
32c9fc6d 39#include <dirent.h>
d212d886 40#include <unistd.h>
32c9fc6d
DWF
41#include "consoles.h"
42
d212d886
DWF
43#ifdef __linux__
44# include <linux/major.h>
45#endif
46
32c9fc6d
DWF
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
58struct console *consoles;
59
d212d886
DWF
60/*
61 * Read and allocate one line from file,
62 * the caller has to free the result
63 */
64static
65#ifdef __GNUC__
66__attribute__((__nonnull__))
67#endif
68char *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';
82out:
83 fclose(fp);
84err:
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 */
93static
94__attribute__((__malloc__))
95char *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;
108out:
109 free(path);
110err:
111 return ret;
112}
113
114/* Read and determine device attribute for tty */
115static
116dev_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);
134out:
135 free(path);
136err:
137 return dev;
138}
139#endif /* __linux__ */
140
32c9fc6d 141static dev_t comparedev;
d212d886
DWF
142static
143#ifdef __GNUC__
144__attribute__((__nonnull__,__malloc__,__hot__))
145#endif
146char* scandev(DIR *dir)
32c9fc6d
DWF
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
d212d886
DWF
171static
172struct chardata initcp = {
173 CERASE,
174 CKILL,
175 CTRL('r'),
176 0
177};
178
179static int concount; /* Counter for console IDs */
180
181static
182#ifdef __GNUC__
183__attribute__((__nonnull__,__hot__))
184#endif
185void 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
209void detect_consoles(const char *console, int fallback)
32c9fc6d 210{
d212d886
DWF
211#ifdef __linux__
212 char *attrib, *cmdline;
32c9fc6d 213 FILE *fc;
d212d886
DWF
214
215 if ((fc = fopen("/proc/consoles", "re"))) {
32c9fc6d
DWF
216 char fbuf[16];
217 int maj, min;
218 DIR *dir;
219 dir = opendir("/dev");
220 if (!dir)
d212d886 221 goto out1;
32c9fc6d 222 while ((fscanf(fc, "%*s %*s (%[^)]) %d:%d", &fbuf[0], &maj, &min) == 3)) {
32c9fc6d
DWF
223 char * name;
224
225 if (!strchr(fbuf, 'E'))
226 continue;
227 comparedev = makedev(maj, min);
d212d886 228
32c9fc6d 229 name = scandev(dir);
d212d886
DWF
230 if (!name)
231 continue;
232 consalloc(name);
233 }
234 closedir(dir);
235out1:
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 }
32c9fc6d 260
d212d886 261 name = scandev(dir);
32c9fc6d
DWF
262 if (!name)
263 continue;
d212d886
DWF
264 consalloc(name);
265 }
266 closedir(dir);
267out2:
268 free(attrib);
269 return;
32c9fc6d 270
d212d886 271 }
32c9fc6d 272
d212d886
DWF
273 if ((cmdline = oneline("/proc/cmdline"))) {
274 char *words= cmdline, *token;
275 DIR *dir;
32c9fc6d 276
d212d886
DWF
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);
32c9fc6d
DWF
336 }
337 closedir(dir);
d212d886
DWF
338out3:
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;
32c9fc6d
DWF
398 }
399}