]> git.ipfire.org Git - thirdparty/util-linux.git/blame - login-utils/sulogin-consoles.c
scriptreplay: cleanup usage()
[thirdparty/util-linux.git] / login-utils / sulogin-consoles.c
CommitLineData
3a08f74f
WF
1/*
2 * consoles.c Routines to detect the system consoles
3 *
4 * Copyright (c) 2011 SuSE LINUX Products GmbH, All rights reserved.
95226b55 5 * Copyright (C) 2012 Karel Zak <kzak@redhat.com>
cae29b37 6 * Copyright (C) 2012 Werner Fink <werner@suse.de>
3a08f74f
WF
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2, or (at your option)
11 * any later version.
95226b55 12 *
3a08f74f
WF
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program (see the file COPYING); if not, write to the
20 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
21 * MA 02110-1301, USA.
22 *
23 * Author: Werner Fink <werner@suse.de>
24 */
25
26#include <limits.h>
27#include <stdio.h>
28#include <stdlib.h>
29#include <string.h>
30#include <sys/types.h>
31#include <sys/stat.h>
32#include <sys/ioctl.h>
33#ifdef __linux__
5ad09239
KZ
34# include <sys/vt.h>
35# include <sys/kd.h>
36# include <linux/serial.h>
37# include <linux/major.h>
3a08f74f 38#endif
3a08f74f 39#include <dirent.h>
cde7699c
WF
40#include <errno.h>
41#include <fcntl.h>
3a08f74f 42#include <unistd.h>
95226b55 43
ab6478ef
WF
44#ifdef USE_SULOGIN_EMERGENCY_MOUNT
45# include <sys/mount.h>
46# include <linux/fs.h>
47# include <linux/magic.h>
ab6478ef
WF
48# ifndef MNT_DETACH
49# define MNT_DETACH 2
50# endif
51#endif
52
95226b55
KZ
53#include "c.h"
54#include "canonicalize.h"
a73f59fa 55#include "sulogin-consoles.h"
3a08f74f 56
3a08f74f
WF
57#if !defined(__STDC_VERSION__) || (__STDC_VERSION__ < 199901L)
58# ifndef typeof
59# define typeof __typeof__
60# endif
61# ifndef restrict
62# define restrict __restrict__
63# endif
64#endif
65
66#define alignof(type) ((sizeof(type)+(sizeof(void*)-1)) & ~(sizeof(void*)-1))
cae29b37 67#define strsize(string) (strlen((string))+1)
3a08f74f 68
b25343bb
KZ
69static int consoles_debug;
70#define DBG(x) do { \
71 if (consoles_debug) { \
72 fputs("consoles debug: ", stderr); \
73 x; \
74 } \
75 } while (0)
76
77static inline void __attribute__ ((__format__ (__printf__, 1, 2)))
71f207a5 78dbgprint(const char * const mesg, ...)
b25343bb
KZ
79{
80 va_list ap;
81 va_start(ap, mesg);
82 vfprintf(stderr, mesg, ap);
83 va_end(ap);
84 fputc('\n', stderr);
85}
86
ab6478ef
WF
87#ifdef USE_SULOGIN_EMERGENCY_MOUNT
88/*
89 * Make C library standard calls such like ttyname(3) work
90 * even if the system does not show any of the standard
91 * directories.
92 */
93
94static uint32_t emergency_flags;
95# define MNT_PROCFS 0x0001
96# define MNT_DEVTMPFS 0x0002
97
ab6478ef
WF
98void emergency_do_umounts(void)
99{
100 if (emergency_flags & MNT_DEVTMPFS)
101 umount2("/dev", MNT_DETACH);
102 if (emergency_flags & MNT_PROCFS)
103 umount2("/proc", MNT_DETACH);
104}
105
ab6478ef
WF
106void emergency_do_mounts(void)
107{
108 struct stat rt, xt;
109
110 if (emergency_flags) {
111 emergency_flags = 0;
112 return;
113 }
114
115 if (stat("/", &rt) != 0) {
223939d9 116 warn("cannot get file status of root file system\n");
ab6478ef
WF
117 return;
118 }
119
120 if (stat("/proc", &xt) == 0
121 && rt.st_dev == xt.st_dev
122 && mount("proc", "/proc", "proc", MS_RELATIME, NULL) == 0)
123 emergency_flags |= MNT_PROCFS;
124
125 if (stat("/dev", &xt) == 0
126 && rt.st_dev == xt.st_dev
127 && mount("devtmpfs", "/dev", "devtmpfs",
128 MS_RELATIME, "mode=0755,nr_inodes=0") == 0) {
129
130 emergency_flags |= MNT_DEVTMPFS;
131 mknod("/dev/console", S_IFCHR|S_IRUSR|S_IWUSR,
132 makedev(TTYAUX_MAJOR, 1));
133
134 if (symlink("/proc/self/fd", "/dev/fd") == 0) {
135 ignore_result( symlink("fd/0", "/dev/stdin") );
136 ignore_result( symlink("fd/1", "/dev/stdout") );
137 ignore_result( symlink("fd/2", "/dev/stderr") );
138 }
139 }
140}
4226f910
KZ
141
142#else /* !USE_SULOGIN_EMERGENCY_MOUNT */
143
144void emergency_do_umounts(void) { }
145void emergency_do_mounts(void) { }
146
ab6478ef
WF
147#endif /* USE_SULOGIN_EMERGENCY_MOUNT */
148
3a08f74f
WF
149/*
150 * Read and allocate one line from file,
151 * the caller has to free the result
152 */
95226b55 153static __attribute__((__nonnull__))
71f207a5 154char *oneline(const char * const file)
3a08f74f
WF
155{
156 FILE *fp;
95226b55 157 char *ret = NULL;
1c8beb3d
GJ
158 size_t dummy = 0;
159 ssize_t len;
3a08f74f 160
b25343bb
KZ
161 DBG(dbgprint("reading %s", file));
162
40042382 163 if (!(fp = fopen(file, "r" UL_CLOEXECSTR)))
95226b55 164 return NULL;
1c8beb3d
GJ
165 len = getline(&ret, &dummy, fp);
166 if (len >= 0) {
95226b55
KZ
167 char *nl;
168
169 if (len)
170 ret[len-1] = '\0';
171 if ((nl = strchr(ret, '\n')))
172 *nl = '\0';
173 }
174
3a08f74f 175 fclose(fp);
3a08f74f
WF
176 return ret;
177}
178
179#ifdef __linux__
180/*
181 * Read and determine active attribute for tty below
182 * /sys/class/tty, the caller has to free the result.
183 */
95226b55 184static __attribute__((__malloc__))
71f207a5 185char *actattr(const char * const tty)
3a08f74f 186{
95226b55 187 char *ret, *path;
3a08f74f 188
95226b55
KZ
189 if (!tty || !*tty)
190 return NULL;
3a08f74f 191 if (asprintf(&path, "/sys/class/tty/%s/active", tty) < 0)
95226b55 192 return NULL;
3a08f74f 193
95226b55 194 ret = oneline(path);
3a08f74f 195 free(path);
3a08f74f
WF
196 return ret;
197}
198
199/*
200 * Read and determine device attribute for tty below
201 * /sys/class/tty.
202 */
203static
71f207a5 204dev_t devattr(const char * const tty)
3a08f74f 205{
3a08f74f
WF
206 dev_t dev = 0;
207 char *path, *value;
208
95226b55
KZ
209 if (!tty || !*tty)
210 return 0;
3a08f74f 211 if (asprintf(&path, "/sys/class/tty/%s/dev", tty) < 0)
95226b55 212 return 0;
3a08f74f 213
95226b55
KZ
214 value = oneline(path);
215 if (value) {
216 unsigned int maj, min;
217
218 if (sscanf(value, "%u:%u", &maj, &min) == 2)
219 dev = makedev(maj, min);
220 free(value);
221 }
3a08f74f 222
3a08f74f 223 free(path);
3a08f74f
WF
224 return dev;
225}
226#endif /* __linux__ */
227
228/*
3fd1f771 229 * Search below /dev for the character device in `dev_t comparedev' variable.
cde7699c
WF
230 * Note that realpath(3) is used here to avoid not existent devices due the
231 * strdup(3) used in our canonicalize_path()!
3a08f74f 232 */
3a08f74f
WF
233static
234#ifdef __GNUC__
235__attribute__((__nonnull__,__malloc__,__hot__))
236#endif
71f207a5 237char* scandev(DIR *dir, const dev_t comparedev)
3a08f74f 238{
cde7699c 239 char path[PATH_MAX];
95226b55 240 char *name = NULL;
71f207a5 241 const struct dirent *dent;
cde7699c 242 int len, fd;
3a08f74f 243
b25343bb
KZ
244 DBG(dbgprint("scanning /dev for %u:%u", major(comparedev), minor(comparedev)));
245
cde7699c
WF
246 /*
247 * Try udev links on character devices first.
248 */
249 if ((len = snprintf(path, sizeof(path),
250 "/dev/char/%u:%u", major(comparedev), minor(comparedev))) > 0 &&
251 (size_t)len < sizeof(path)) {
252
253 name = realpath(path, NULL);
254 if (name)
255 goto out;
256 }
257
3a08f74f
WF
258 fd = dirfd(dir);
259 rewinddir(dir);
260 while ((dent = readdir(dir))) {
3a08f74f 261 struct stat st;
3deb67f5
KZ
262
263#ifdef _DIRENT_HAVE_D_TYPE
264 if (dent->d_type != DT_UNKNOWN && dent->d_type != DT_CHR)
265 continue;
266#endif
3a08f74f
WF
267 if (fstatat(fd, dent->d_name, &st, 0) < 0)
268 continue;
269 if (!S_ISCHR(st.st_mode))
270 continue;
271 if (comparedev != st.st_rdev)
272 continue;
cde7699c
WF
273 if ((len = snprintf(path, sizeof(path), "/dev/%s", dent->d_name)) < 0 ||
274 (size_t)len >= sizeof(path))
3a08f74f 275 continue;
ab6478ef 276
cde7699c
WF
277 name = realpath(path, NULL);
278 if (name)
279 goto out;
3a08f74f 280 }
95226b55 281
cde7699c
WF
282#ifdef USE_SULOGIN_EMERGENCY_MOUNT
283 /*
284 * There was no /dev mounted hence and no device was found hence we create our own.
285 */
286 if (!name && (emergency_flags & MNT_DEVTMPFS)) {
287
288 if ((len = snprintf(path, sizeof(path),
289 "/dev/tmp-%u:%u", major(comparedev), minor(comparedev))) < 0 ||
290 (size_t)len >= sizeof(path))
291 goto out;
292
293 if (mknod(path, S_IFCHR|S_IRUSR|S_IWUSR, comparedev) < 0 && errno != EEXIST)
294 goto out;
295
296 name = realpath(path, NULL);
297 }
298#endif
299out:
3a08f74f
WF
300 return name;
301}
302
303/*
304 * Default control characters for an unknown terminal line.
305 */
3a08f74f
WF
306
307/*
308 * Allocate an aligned `struct console' memory area,
309 * initialize its default values, and append it to
310 * the global linked list.
311 */
3a08f74f
WF
312static
313#ifdef __GNUC__
b236e43e 314__attribute__((__hot__))
3a08f74f 315#endif
71f207a5 316int append_console(struct list_head *consoles, const char * const name)
3a08f74f
WF
317{
318 struct console *restrict tail;
71f207a5 319 const struct console *last = NULL;
53e0a688 320
b25343bb
KZ
321 DBG(dbgprint("appenging %s", name));
322
cae29b37
WF
323 if (!list_empty(consoles))
324 last = list_last_entry(consoles, struct console, entry);
325
326 if (posix_memalign((void *) &tail, sizeof(void *),
327 alignof(struct console) + strsize(name)) != 0)
f0d6004c 328 return -ENOMEM;
3a08f74f 329
cae29b37 330 INIT_LIST_HEAD(&tail->entry);
f5664477 331 INIT_CHARDATA(&tail->cp);
6c7c1eaf 332
cae29b37
WF
333 list_add_tail(&tail->entry, consoles);
334 tail->tty = ((char *) tail) + alignof(struct console);
335 strcpy(tail->tty, name);
3a08f74f
WF
336
337 tail->file = (FILE*)0;
338 tail->flags = 0;
339 tail->fd = -1;
6c7c1eaf 340 tail->id = last ? last->id + 1 : 0;
cde7699c 341 tail->pid = -1;
3a08f74f 342 memset(&tail->tio, 0, sizeof(tail->tio));
3a08f74f 343
f0d6004c 344 return 0;
3a08f74f
WF
345}
346
aded518a
KZ
347#ifdef __linux__
348/*
349 * return codes:
350 * < 0 - fatal error (no mem or so... )
351 * 0 - success
352 * 1 - recoverable error
353 * 2 - detection not available
354 */
cae29b37 355static int detect_consoles_from_proc(struct list_head *consoles)
aded518a
KZ
356{
357 char fbuf[16 + 1];
358 DIR *dir = NULL;
359 FILE *fc = NULL;
624b204d 360 int maj, min, rc = 1, matches;
aded518a 361
b25343bb 362 DBG(dbgprint("trying /proc"));
aded518a 363
40042382 364 fc = fopen("/proc/consoles", "r" UL_CLOEXECSTR);
b25343bb
KZ
365 if (!fc) {
366 rc = 2;
367 goto done;
368 }
aded518a
KZ
369 dir = opendir("/dev");
370 if (!dir)
371 goto done;
372
624b204d 373 while ((matches = fscanf(fc, "%*s %*s (%16[^)]) %d:%d", fbuf, &maj, &min)) >= 1) {
aded518a
KZ
374 char *name;
375 dev_t comparedev;
376
624b204d
WF
377 if (matches != 3)
378 continue;
aded518a
KZ
379 if (!strchr(fbuf, 'E'))
380 continue;
381 comparedev = makedev(maj, min);
382 name = scandev(dir, comparedev);
383 if (!name)
384 continue;
385 rc = append_console(consoles, name);
cae29b37 386 free(name);
aded518a
KZ
387 if (rc < 0)
388 goto done;
389 }
390
cae29b37 391 rc = list_empty(consoles) ? 1 : 0;
aded518a
KZ
392done:
393 if (dir)
394 closedir(dir);
395 if (fc)
396 fclose(fc);
b25343bb 397 DBG(dbgprint("[/proc rc=%d]", rc));
aded518a
KZ
398 return rc;
399}
615eada9
KZ
400
401/*
402 * return codes:
403 * < 0 - fatal error (no mem or so... )
404 * 0 - success
405 * 1 - recoverable error
406 * 2 - detection not available
407 */
cae29b37 408static int detect_consoles_from_sysfs(struct list_head *consoles)
615eada9
KZ
409{
410 char *attrib = NULL, *words, *token;
411 DIR *dir = NULL;
412 int rc = 1;
413
b25343bb
KZ
414 DBG(dbgprint("trying /sys"));
415
615eada9 416 attrib = actattr("console");
b25343bb
KZ
417 if (!attrib) {
418 rc = 2;
419 goto done;
420 }
615eada9
KZ
421
422 words = attrib;
423
424 dir = opendir("/dev");
425 if (!dir)
426 goto done;
427
428 while ((token = strsep(&words, " \t\r\n"))) {
429 char *name;
430 dev_t comparedev;
431
432 if (*token == '\0')
433 continue;
434
435 comparedev = devattr(token);
436 if (comparedev == makedev(TTY_MAJOR, 0)) {
437 char *tmp = actattr(token);
438 if (!tmp)
439 continue;
440 comparedev = devattr(tmp);
441 free(tmp);
442 }
443
444 name = scandev(dir, comparedev);
445 if (!name)
446 continue;
447 rc = append_console(consoles, name);
cae29b37 448 free(name);
615eada9
KZ
449 if (rc < 0)
450 goto done;
451 }
452
cae29b37 453 rc = list_empty(consoles) ? 1 : 0;
615eada9
KZ
454done:
455 free(attrib);
456 if (dir)
457 closedir(dir);
b25343bb 458 DBG(dbgprint("[/sys rc=%d]", rc));
615eada9
KZ
459 return rc;
460}
461
4fcee23d 462
cae29b37 463static int detect_consoles_from_cmdline(struct list_head *consoles)
4fcee23d
KZ
464{
465 char *cmdline, *words, *token;
466 dev_t comparedev;
b25343bb 467 DIR *dir = NULL;
4fcee23d
KZ
468 int rc = 1, fd;
469
b25343bb
KZ
470 DBG(dbgprint("trying kernel cmdline"));
471
4fcee23d 472 cmdline = oneline("/proc/cmdline");
b25343bb
KZ
473 if (!cmdline) {
474 rc = 2;
475 goto done;
476 }
4fcee23d
KZ
477
478 words= cmdline;
479 dir = opendir("/dev");
480 if (!dir)
481 goto done;
482
483 while ((token = strsep(&words, " \t\r\n"))) {
484#ifdef TIOCGDEV
485 unsigned int devnum;
486#else
487 struct vt_stat vt;
488 struct stat st;
489#endif
490 char *colon, *name;
491
492 if (*token != 'c')
493 continue;
494 if (strncmp(token, "console=", 8) != 0)
495 continue;
496 token += 8;
497
498 if (strcmp(token, "brl") == 0)
499 token += 4;
500 if ((colon = strchr(token, ',')))
501 *colon = '\0';
502
503 if (asprintf(&name, "/dev/%s", token) < 0)
504 continue;
505 if ((fd = open(name, O_RDWR|O_NONBLOCK|O_NOCTTY|O_CLOEXEC)) < 0) {
506 free(name);
507 continue;
508 }
509 free(name);
510#ifdef TIOCGDEV
511 if (ioctl (fd, TIOCGDEV, &devnum) < 0) {
512 close(fd);
513 continue;
514 }
515 comparedev = (dev_t) devnum;
516#else
517 if (fstat(fd, &st) < 0) {
518 close(fd);
519 continue;
520 }
521 comparedev = st.st_rdev;
522 if (comparedev == makedev(TTY_MAJOR, 0)) {
523 if (ioctl(fd, VT_GETSTATE, &vt) < 0) {
524 close(fd);
525 continue;
526 }
527 comparedev = makedev(TTY_MAJOR, (int)vt.v_active);
528 }
529#endif
530 close(fd);
531
532 name = scandev(dir, comparedev);
533 if (!name)
534 continue;
535 rc = append_console(consoles, name);
cae29b37 536 free(name);
4fcee23d
KZ
537 if (rc < 0)
538 goto done;
539 }
540
cae29b37 541 rc = list_empty(consoles) ? 1 : 0;
4fcee23d
KZ
542done:
543 if (dir)
544 closedir(dir);
545 free(cmdline);
b25343bb 546 DBG(dbgprint("[kernel cmdline rc=%d]", rc));
4fcee23d
KZ
547 return rc;
548}
549
a303e047 550#ifdef TIOCGDEV
cae29b37 551static int detect_consoles_from_tiocgdev(struct list_head *consoles,
71f207a5 552 const int fallback,
6ea0921e
KZ
553 const char *device)
554{
6ea0921e
KZ
555 unsigned int devnum;
556 char *name;
557 int rc = 1, fd = -1;
558 dev_t comparedev;
559 DIR *dir = NULL;
cae29b37 560 struct console *console;
6ea0921e 561
b25343bb
KZ
562 DBG(dbgprint("trying tiocgdev"));
563
6ea0921e
KZ
564 if (!device || !*device)
565 fd = dup(fallback);
566 else
567 fd = open(device, O_RDWR|O_NONBLOCK|O_NOCTTY|O_CLOEXEC);
568
569 if (fd < 0)
570 goto done;
571 if (ioctl (fd, TIOCGDEV, &devnum) < 0)
572 goto done;
573
574 comparedev = (dev_t) devnum;
575 dir = opendir("/dev");
576 if (!dir)
577 goto done;
578
579 name = scandev(dir, comparedev);
580 closedir(dir);
581
582 if (!name) {
583 name = (char *) (device && *device ? device : ttyname(fallback));
584 if (!name)
585 name = "/dev/tty1";
586
587 name = strdup(name);
588 if (!name) {
589 rc = -ENOMEM;
590 goto done;
591 }
592 }
593 rc = append_console(consoles, name);
cae29b37 594 free(name);
6ea0921e
KZ
595 if (rc < 0)
596 goto done;
cae29b37
WF
597 if (list_empty(consoles)) {
598 rc = 1;
599 goto done;
600 }
601 console = list_last_entry(consoles, struct console, entry);
602 if (console && (!device || !*device))
603 console->fd = fallback;
6ea0921e
KZ
604done:
605 if (fd >= 0)
606 close(fd);
b25343bb 607 DBG(dbgprint("[tiocgdev rc=%d]", rc));
6ea0921e 608 return rc;
6ea0921e 609}
a303e047 610#endif /* TIOCGDEV */
aded518a
KZ
611#endif /* __linux__ */
612
3a08f74f
WF
613/*
614 * Try to detect the real device(s) used for the system console
615 * /dev/console if but only if /dev/console is used. On Linux
616 * this can be more than one device, e.g. a serial line as well
617 * as a virtual console as well as a simple printer.
618 *
619 * Returns 1 if stdout and stderr should be reconnected and 0
f0d6004c 620 * otherwise or less than zero on error.
3a08f74f 621 */
71f207a5 622int detect_consoles(const char *device, const int fallback, struct list_head *consoles)
3a08f74f 623{
97909f61 624 int fd, reconnect = 0, rc;
e0e5974f 625 dev_t comparedev = 0;
4fcee23d 626
b25343bb
KZ
627 consoles_debug = getenv("CONSOLES_DEBUG") ? 1 : 0;
628
95226b55 629 if (!device || !*device)
863d371c 630 fd = fallback >= 0 ? dup(fallback) : - 1;
3a08f74f
WF
631 else {
632 fd = open(device, O_RDWR|O_NONBLOCK|O_NOCTTY|O_CLOEXEC);
97909f61 633 reconnect = 1;
3a08f74f
WF
634 }
635
b25343bb
KZ
636 DBG(dbgprint("detection started [device=%s, fallback=%d]",
637 device, fallback));
638
3a08f74f
WF
639 if (fd >= 0) {
640 DIR *dir;
641 char *name;
642 struct stat st;
643#ifdef TIOCGDEV
644 unsigned int devnum;
99eadc9e
JC
645#endif
646#ifdef __GNU__
647 /*
648 * The Hurd always gives st_rdev as 0, which causes this
649 * method to select the first terminal it finds.
650 */
651 close(fd);
652 goto fallback;
3a08f74f 653#endif
b25343bb 654 DBG(dbgprint("trying device/fallback file descriptor"));
3a08f74f
WF
655
656 if (fstat(fd, &st) < 0) {
657 close(fd);
658 goto fallback;
659 }
660 comparedev = st.st_rdev;
661
97909f61
KZ
662 if (reconnect &&
663 (fstat(fallback, &st) < 0 || comparedev != st.st_rdev))
3a08f74f
WF
664 dup2(fd, fallback);
665#ifdef __linux__
666 /*
667 * Check if the device detection for Linux system console should be used.
668 */
669 if (comparedev == makedev(TTYAUX_MAJOR, 0)) { /* /dev/tty */
670 close(fd);
671 device = "/dev/tty";
672 goto fallback;
673 }
674 if (comparedev == makedev(TTYAUX_MAJOR, 1)) { /* /dev/console */
675 close(fd);
676 goto console;
677 }
678 if (comparedev == makedev(TTYAUX_MAJOR, 2)) { /* /dev/ptmx */
679 close(fd);
680 device = "/dev/tty";
681 goto fallback;
682 }
683 if (comparedev == makedev(TTY_MAJOR, 0)) { /* /dev/tty0 */
684 struct vt_stat vt;
685 if (ioctl(fd, VT_GETSTATE, &vt) < 0) {
686 close(fd);
687 goto fallback;
688 }
689 comparedev = makedev(TTY_MAJOR, (int)vt.v_active);
690 }
691#endif
692#ifdef TIOCGDEV
693 if (ioctl (fd, TIOCGDEV, &devnum) < 0) {
694 close(fd);
695 goto fallback;
696 }
697 comparedev = (dev_t)devnum;
698#endif
699 close(fd);
700 dir = opendir("/dev");
701 if (!dir)
702 goto fallback;
e0e5974f 703 name = scandev(dir, comparedev);
874dbb9c
KZ
704 closedir(dir);
705
f0d6004c
KZ
706 if (name) {
707 rc = append_console(consoles, name);
cae29b37 708 free(name);
f0d6004c
KZ
709 if (rc < 0)
710 return rc;
711 }
cae29b37 712 if (list_empty(consoles))
3a08f74f 713 goto fallback;
874dbb9c
KZ
714
715 DBG(dbgprint("detection success [rc=%d]", reconnect));
97909f61 716 return reconnect;
3a08f74f
WF
717 }
718#ifdef __linux__
719console:
720 /*
9e930041 721 * Detection of devices used for Linux system console using
3a08f74f
WF
722 * the /proc/consoles API with kernel 2.6.38 and higher.
723 */
aded518a
KZ
724 rc = detect_consoles_from_proc(consoles);
725 if (rc == 0)
726 return reconnect; /* success */
727 if (rc < 0)
728 return rc; /* fatal error */
3a08f74f 729
3a08f74f
WF
730 /*
731 * Detection of devices used for Linux system console using
732 * the sysfs /sys/class/tty/ API with kernel 2.6.37 and higher.
733 */
615eada9
KZ
734 rc = detect_consoles_from_sysfs(consoles);
735 if (rc == 0)
736 return reconnect; /* success */
737 if (rc < 0)
738 return rc; /* fatal error */
3a08f74f 739
3a08f74f
WF
740 /*
741 * Detection of devices used for Linux system console using
742 * kernel parameter on the kernels command line.
743 */
4fcee23d
KZ
744 rc = detect_consoles_from_cmdline(consoles);
745 if (rc == 0)
746 return reconnect; /* success */
747 if (rc < 0)
748 return rc; /* fatal error */
3a08f74f 749
6ea0921e
KZ
750 /*
751 * Detection of the device used for Linux system console using
752 * the ioctl TIOCGDEV if available (e.g. official 2.6.38).
753 */
a303e047 754#ifdef TIOCGDEV
6ea0921e
KZ
755 rc = detect_consoles_from_tiocgdev(consoles, fallback, device);
756 if (rc == 0)
757 return reconnect; /* success */
758 if (rc < 0)
759 return rc; /* fatal error */
a303e047 760#endif
cae29b37 761 if (!list_empty(consoles)) {
27a76b7b
KZ
762 DBG(dbgprint("detection success [rc=%d]", reconnect));
763 return reconnect;
764 }
b25343bb 765
3a08f74f 766#endif /* __linux __ */
6ea0921e 767
3a08f74f
WF
768fallback:
769 if (fallback >= 0) {
8bd64503
KZ
770 const char *name;
771 char *n;
cae29b37 772 struct console *console;
95226b55 773
3a08f74f
WF
774 if (device && *device != '\0')
775 name = device;
776 else name = ttyname(fallback);
777
778 if (!name)
779 name = "/dev/tty";
780
21c4058d
SK
781 n = strdup(name);
782 if (!n)
783 return -ENOMEM;
784 rc = append_console(consoles, n);
785 free(n);
f0d6004c
KZ
786 if (rc < 0)
787 return rc;
cae29b37
WF
788 if (list_empty(consoles))
789 return 1;
790 console = list_last_entry(consoles, struct console, entry);
791 if (console)
792 console->fd = fallback;
3a08f74f 793 }
b25343bb
KZ
794
795 DBG(dbgprint("detection done by fallback [rc=%d]", reconnect));
97909f61 796 return reconnect;
3a08f74f
WF
797}
798
82d11fbd
KZ
799
800#ifdef TEST_PROGRAM
801int main(int argc, char *argv[])
802{
803 char *name = NULL;
804 int fd, re;
5d74cf00 805 struct list_head *p, consoles;
82d11fbd
KZ
806
807 if (argc == 2) {
808 name = argv[1];
809 fd = open(name, O_RDWR);
810 } else {
811 name = ttyname(STDIN_FILENO);
812 fd = STDIN_FILENO;
813 }
814
815 if (!name)
816 errx(EXIT_FAILURE, "usage: %s [<tty>]\n", program_invocation_short_name);
817
5d74cf00 818 INIT_LIST_HEAD(&consoles);
82d11fbd
KZ
819 re = detect_consoles(name, fd, &consoles);
820
cae29b37
WF
821 list_for_each(p, &consoles) {
822 struct console *c = list_entry(p, struct console, entry);
823 printf("%s: id=%d %s\n", c->tty, c->id, re ? "(reconnect) " : "");
824 }
82d11fbd
KZ
825
826 return 0;
827}
828#endif