"Fossies" - the Fresh Open Source Software Archive

Member "sysvinit-2.99/src/consoles.c" (21 Feb 2021, 10347 Bytes) of package /linux/misc/sysvinit-2.99.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "consoles.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 2.98_vs_2.99.

    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 #include <sys/ttydefaults.h>
   32 #ifdef __linux__
   33 #  include <sys/vt.h>
   34 #  include <sys/kd.h>
   35 #  include <linux/serial.h>
   36 #include <sys/sysmacros.h>  
   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 below
   91  *  /sys/class/tty, 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 /*
  115  * Read and determine device attribute for tty below
  116  * /sys/class/tty.
  117  */
  118 static
  119 dev_t devattr(const char *tty)
  120 {
  121     unsigned int maj, min;
  122     dev_t dev = 0;
  123     char *path, *value;
  124 
  125     if (!tty || *tty == '\0')
  126         goto err;
  127 
  128     if (asprintf(&path, "/sys/class/tty/%s/dev", tty) < 0)
  129         goto err;
  130 
  131     if ((value = oneline(path)) == (char*)0)
  132         goto out;
  133 
  134     if (sscanf(value, "%u:%u", &maj, &min) == 2)
  135         dev = makedev(maj, min);
  136     free(value);
  137 out:
  138     free(path);
  139 err:
  140     return dev;
  141 }
  142 #endif /* __linux__ */
  143 
  144 /*
  145  * Search below /dev for the character device in
  146  * the local `dev_t comparedev' variable.
  147  */
  148 static dev_t comparedev;
  149 static
  150 #ifdef __GNUC__
  151 __attribute__((__nonnull__,__malloc__,__hot__))
  152 #endif
  153 char* scandev(DIR *dir)
  154 {
  155     char *name = (char*)0;
  156     struct dirent *dent;
  157     int fd;
  158 
  159     fd = dirfd(dir);
  160     rewinddir(dir);
  161     while ((dent = readdir(dir))) {
  162         char *path;
  163         struct stat st;
  164         if (fstatat(fd, dent->d_name, &st, 0) < 0)
  165             continue;
  166         if (!S_ISCHR(st.st_mode))
  167             continue;
  168         if (comparedev != st.st_rdev)
  169             continue;
  170         if (asprintf(&path, "/dev/%s", dent->d_name) < 0)
  171             continue;
  172         name = realpath(path, NULL);
  173         free(path);
  174         break;
  175     }
  176     return name;
  177 }
  178 
  179 /*
  180  * Default control characters for an unknown terminal line.
  181  */
  182 static
  183 struct chardata initcp = {
  184     CERASE,
  185     CKILL,
  186     CTRL('r'),
  187     0
  188 };
  189 
  190 /*
  191  * Allocate an aligned `struct console' memory area,
  192  * initialize its default values, and append it to
  193  * the global linked list.
  194  */
  195 
  196 static int concount;        /* Counter for console IDs */
  197 
  198 static
  199 #ifdef __GNUC__
  200 __attribute__((__nonnull__,__hot__))
  201 #endif
  202 void consalloc(char * name)
  203 {
  204     struct console *restrict tail;
  205 
  206     if (posix_memalign((void*)&tail, sizeof(void*), alignof(typeof(struct console))) != 0)
  207         perror("memory allocation");
  208 
  209     tail->next = (struct console*)0;
  210     tail->tty = name;
  211 
  212     tail->file = (FILE*)0;
  213     tail->flags = 0;
  214     tail->fd = -1;
  215     tail->id = concount++;
  216     tail->pid = 0;
  217     memset(&tail->tio, 0, sizeof(tail->tio));
  218     memcpy(&tail->cp, &initcp, sizeof(struct chardata));
  219 
  220     if (!consoles)
  221         consoles = tail;
  222     else
  223         consoles->next = tail;
  224 }
  225 
  226 /*
  227  * Try to detect the real device(s) used for the system console
  228  * /dev/console if but only if /dev/console is used.  On Linux
  229  * this can be more than one device, e.g. a serial line as well
  230  * as a virtual console as well as a simple printer.
  231  *
  232  * Returns 1 if stdout and stderr should be reconnected and 0
  233  * otherwise.
  234  */
  235 int detect_consoles(const char *device, int fallback)
  236 {
  237     int fd, ret = 0;
  238 #ifdef __linux__
  239     char *attrib, *cmdline;
  240     FILE *fc;
  241 #endif
  242     if (!device || *device == '\0')
  243         fd = dup(fallback);
  244     else {
  245         fd = open(device, O_RDWR|O_NONBLOCK|O_NOCTTY|O_CLOEXEC);
  246         ret = 1;
  247     }
  248 
  249     if (fd >= 0) {
  250         DIR *dir;
  251         char *name;
  252         struct stat st;
  253 #ifdef TIOCGDEV
  254         unsigned int devnum;
  255 #endif
  256 
  257         if (fstat(fd, &st) < 0) {
  258             close(fd);
  259             goto fallback;
  260         }
  261         comparedev = st.st_rdev;
  262 
  263         if (ret && (fstat(fallback, &st) < 0 || comparedev != st.st_rdev))
  264             dup2(fd, fallback);
  265 #ifdef __linux__
  266         /*
  267          * Check if the device detection for Linux system console should be used.
  268          */
  269         if (comparedev == makedev(TTYAUX_MAJOR, 0)) {   /* /dev/tty */
  270             close(fd);
  271             device = "/dev/tty";
  272             goto fallback;
  273         }
  274         if (comparedev == makedev(TTYAUX_MAJOR, 1)) {   /* /dev/console */
  275             close(fd);
  276             goto console;
  277         }
  278         if (comparedev == makedev(TTYAUX_MAJOR, 2)) {   /* /dev/ptmx    */
  279             close(fd);
  280             device = "/dev/tty";
  281             goto fallback;
  282         }
  283         if (comparedev == makedev(TTY_MAJOR, 0)) {  /* /dev/tty0    */
  284             struct vt_stat vt;
  285             if (ioctl(fd, VT_GETSTATE, &vt) < 0) {
  286                 close(fd);
  287                 goto fallback;
  288             }
  289             comparedev = makedev(TTY_MAJOR, (int)vt.v_active);
  290         }
  291 #endif
  292 #ifdef TIOCGDEV
  293         if (ioctl (fd, TIOCGDEV, &devnum) < 0) {
  294             close(fd);
  295             goto fallback;
  296         }
  297         comparedev = (dev_t)devnum;
  298 #endif
  299         close(fd);
  300         dir = opendir("/dev");
  301         if (!dir)
  302             goto fallback;
  303         name = scandev(dir);
  304         if (name)
  305             consalloc(name);
  306         closedir(dir);
  307         if (!consoles)
  308             goto fallback;
  309         return ret;
  310     }
  311 #ifdef __linux__
  312 console:
  313     /*
  314      * Detection of devices used for Linux system consolei using
  315      * the /proc/consoles API with kernel 2.6.38 and higher.
  316      */
  317     if ((fc = fopen("/proc/consoles", "re"))) {
  318         char fbuf[16];
  319         int maj, min;
  320         DIR *dir;
  321         dir = opendir("/dev");
  322         if (!dir) {
  323             fclose(fc);
  324             goto fallback;
  325         }
  326         while ((fscanf(fc, "%*s %*s (%[^)]) %d:%d", &fbuf[0], &maj, &min) == 3)) {
  327             char * name;
  328 
  329             if (!strchr(fbuf, 'E'))
  330                 continue;
  331             comparedev = makedev(maj, min);
  332 
  333             name = scandev(dir);
  334             if (!name)
  335                 continue;
  336             consalloc(name);
  337         }
  338         closedir(dir);
  339         fclose(fc);
  340         return ret;
  341     }
  342     /*
  343      * Detection of devices used for Linux system console using
  344      * the sysfs /sys/class/tty/ API with kernel 2.6.37 and higher.
  345      */
  346     if ((attrib = actattr("console"))) {
  347         char *words = attrib, *token;
  348         DIR *dir;
  349 
  350         dir = opendir("/dev");
  351         if (!dir) {
  352             free(attrib);
  353             goto fallback;
  354         }
  355         while ((token = strsep(&words, " \t\r\n"))) {
  356             char * name;
  357 
  358             if (*token == '\0')
  359                 continue;
  360             comparedev = devattr(token);
  361             if (comparedev == makedev(TTY_MAJOR, 0)) {
  362                 char *tmp = actattr(token);
  363                 if (!tmp)
  364                     continue;
  365                 comparedev = devattr(tmp);
  366                 free(tmp);
  367             }
  368 
  369             name = scandev(dir);
  370             if (!name)
  371                 continue;
  372             consalloc(name);
  373         }
  374         closedir(dir);
  375         free(attrib);
  376         if (!consoles)
  377             goto fallback;
  378         return ret;
  379 
  380     }
  381     /*
  382      * Detection of devices used for Linux system console using
  383      * kernel parameter on the kernels command line.
  384      */
  385     if ((cmdline = oneline("/proc/cmdline"))) {
  386         char *words= cmdline, *token;
  387         DIR *dir;
  388 
  389         dir = opendir("/dev");
  390         if (!dir) {
  391             free(cmdline);
  392             goto fallback;
  393         }
  394         while ((token = strsep(&words, " \t\r\n"))) {
  395 #ifdef TIOCGDEV
  396             unsigned int devnum;
  397 #else
  398             struct vt_stat vt;
  399             struct stat st;
  400 #endif
  401             char *colon, *name;
  402 
  403             if (*token != 'c')
  404                 continue;
  405 
  406             if (strncmp(token, "console=", 8) != 0)
  407                 continue;
  408             token += 8;
  409 
  410             if (strcmp(token, "brl") == 0)
  411                 token += 4;
  412             if ((colon = strchr(token, ',')))
  413                 *colon = '\0';
  414 
  415             if (asprintf(&name, "/dev/%s", token) < 0)
  416                 continue;
  417 
  418             if ((fd = open(name, O_RDWR|O_NONBLOCK|O_NOCTTY|O_CLOEXEC)) < 0) {
  419                 free(name);
  420                 continue;
  421             }
  422             free(name);
  423 #ifdef TIOCGDEV
  424             if (ioctl (fd, TIOCGDEV, &devnum) < 0) {
  425                 close(fd);
  426                 continue;
  427             }
  428             comparedev = (dev_t)devnum;
  429 #else
  430             if (fstat(fd, &st) < 0) {
  431                 close(fd);
  432                 continue;
  433             }
  434             comparedev = st.st_rdev;
  435             if (comparedev == makedev(TTY_MAJOR, 0)) {
  436                 if (ioctl(fd, VT_GETSTATE, &vt) < 0) {
  437                     close(fd);
  438                     continue;
  439                 }
  440                 comparedev = makedev(TTY_MAJOR, (int)vt.v_active);
  441             }
  442 #endif
  443             close(fd);
  444 
  445             name = scandev(dir);
  446             if (!name)
  447                 continue;
  448             consalloc(name);
  449         }
  450         closedir(dir);
  451         free(cmdline);
  452         /*
  453          * Detection of the device used for Linux system console using
  454          * the ioctl TIOCGDEV if available (e.g. official 2.6.38).
  455          */
  456         if (!consoles) {
  457 #ifdef TIOCGDEV
  458             unsigned int devnum;
  459             const char *name;
  460 
  461             if (!device || *device == '\0')
  462                 fd = dup(fallback);
  463             else    fd = open(device, O_RDWR|O_NONBLOCK|O_NOCTTY|O_CLOEXEC);
  464 
  465             if (fd < 0)
  466                 goto fallback;
  467 
  468             if (ioctl (fd, TIOCGDEV, &devnum) < 0) {
  469                 close(fd);
  470                 goto fallback;
  471             }
  472             comparedev = (dev_t)devnum;
  473             close(fd);
  474 
  475             if (device && *device != '\0')
  476                 name = device;
  477             else    name = ttyname(fallback);
  478 
  479             if (!name)
  480                 name = "/dev/tty1";
  481 
  482             consalloc(strdup(name));
  483             if (consoles) {
  484                 if (!device || *device == '\0')
  485                     consoles->fd = fallback;
  486                 return ret;
  487             }
  488 #endif
  489             goto fallback;
  490         }
  491         return ret;
  492     }
  493 #endif /* __linux __ */
  494 fallback:
  495     if (fallback >= 0) {
  496         const char *name;
  497         
  498         if (device && *device != '\0')
  499             name = device;
  500         else    name = ttyname(fallback);
  501 
  502         if (!name)
  503             name = "/dev/tty";
  504 
  505         consalloc(strdup(name));
  506         if (consoles)
  507             consoles->fd = fallback;
  508     }
  509     return ret;
  510 }