1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License, Version 1.0 only
   6  * (the "License").  You may not use this file except in compliance
   7  * with the License.
   8  *
   9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  10  * or http://www.opensolaris.org/os/licensing.
  11  * See the License for the specific language governing permissions
  12  * and limitations under the License.
  13  *
  14  * When distributing Covered Code, include this CDDL HEADER in each
  15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  16  * If applicable, add the following below this CDDL HEADER, with the
  17  * fields enclosed by brackets "[]" replaced with your own identifying
  18  * information: Portions Copyright [yyyy] [name of copyright owner]
  19  *
  20  * CDDL HEADER END
  21  */
  22 /*
  23  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
  24  * Use is subject to license terms.
  25  */
  26 
  27 /*
  28  * Implementation to get PORT nodes state and condition information
  29  */
  30 #include <stdio.h>
  31 #include <stdlib.h>
  32 #include <ctype.h>
  33 #include <strings.h>
  34 #include <fcntl.h>
  35 #include <unistd.h>
  36 #include <stropts.h>
  37 #include <locale.h>
  38 #include <syslog.h>
  39 #include <sys/types.h>
  40 #include <sys/termios.h>
  41 #include <sys/stat.h>
  42 #include <fcntl.h>
  43 #include <kstat.h>
  44 #include <signal.h>
  45 #include <assert.h>
  46 #include <config_admin.h>
  47 
  48 #include <picl.h>
  49 #include "piclfrutree.h"
  50 
  51 #define LINK_UP         "link_up"
  52 #define DUPLEX          "duplex"
  53 #define IF_SPEED        "ifspeed"
  54 #define IERRORS         "ierrors"
  55 #define IPACKETS        "ipackets"
  56 #define OERRORS         "oerrors"
  57 #define OPACKETS        "opackets"
  58 #define NOCANPUT        "nocanput"
  59 #define RUNT_ERRORS     "runt_errors"
  60 #define COLLISIONS      "collisions"
  61 
  62 typedef int (*funcp)(kstat_ctl_t *, char *, int);
  63 
  64 static kstat_named_t *kstat_name_lookup(kstat_ctl_t *, char *, int, char *);
  65 static int kstat_network_port_state(kstat_ctl_t *kc, char *, int);
  66 static int kstat_network_port_cond(kstat_ctl_t *kc, char *, int);
  67 static int serial_port_state(kstat_ctl_t *, char *, int);
  68 static int serial_port_cond(kstat_ctl_t *kc, char *, int);
  69 static int parallel_port_state(kstat_ctl_t *, char *, int);
  70 static int parallel_port_cond(kstat_ctl_t *kc, char *, int);
  71 static void             sig_alarm_handler(int);
  72 
  73 static funcp port_state[] = {
  74         kstat_network_port_state,
  75         serial_port_state,
  76         parallel_port_state
  77 };
  78 
  79 static funcp port_cond[] = {
  80         kstat_network_port_cond,
  81         serial_port_cond,
  82         parallel_port_cond
  83 };
  84 
  85 /*
  86  * kstat_port_state: returns ethernet, or serial, or parallel port status
  87  * 1 = up, 0 = down, anything else = unknown
  88  */
  89 int
  90 kstat_port_state(frutree_port_type_t port_type, char *driver_name,
  91         int driver_instance)
  92 {
  93         int rc = -1;
  94         kstat_ctl_t     *kc = NULL;
  95 
  96         switch (port_type) {
  97         case NETWORK_PORT:
  98         case SERIAL_PORT:
  99         case PARALLEL_PORT:
 100                 if ((kc = kstat_open()) == NULL) {
 101                         return (-1);
 102                 }
 103                 rc = port_state[port_type](kc, driver_name, driver_instance);
 104                 kstat_close(kc);
 105                 return (rc);
 106         default:
 107                 return (-1);
 108         }
 109 }
 110 
 111 /*
 112  * kstat_port_cond: returns ethernet, or serial, or parallel port condition
 113  */
 114 int
 115 kstat_port_cond(frutree_port_type_t port_type, char *driver_name,
 116         int driver_instance)
 117 {
 118         int rc = -1;
 119         kstat_ctl_t     *kc = NULL;
 120         switch (port_type) {
 121         case NETWORK_PORT:
 122         case SERIAL_PORT:
 123         case PARALLEL_PORT:
 124                 if ((kc = kstat_open()) == NULL) {
 125                         return (-1);
 126                 }
 127                 rc = port_cond[port_type](kc, driver_name, driver_instance);
 128                 kstat_close(kc);
 129                 return (rc);
 130         default:
 131                 return (-1);
 132         }
 133 }
 134 
 135 static kstat_named_t *
 136 kstat_name_lookup(kstat_ctl_t *kc, char *ks_module, int ks_instance, char *name)
 137 {
 138         kstat_t         *ksp;
 139 
 140         assert(kc);
 141         assert(ks_module);
 142         assert(name);
 143 
 144         for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) {
 145                 if (strcmp(ksp->ks_module, ks_module) == 0 &&
 146                     ksp->ks_instance == ks_instance &&
 147                     ksp->ks_type == KSTAT_TYPE_NAMED &&
 148                     kstat_read(kc, ksp, NULL) != -1 &&
 149                     kstat_data_lookup(ksp, name)) {
 150 
 151                         ksp = kstat_lookup(kc, ks_module, ks_instance,
 152                             ksp->ks_name);
 153                         if (!ksp)
 154                                 return (NULL);
 155                         if (kstat_read(kc, ksp, NULL) == -1)
 156                                 return (NULL);
 157                         return ((kstat_named_t *)kstat_data_lookup(ksp, name));
 158                 }
 159         }
 160         return (NULL);
 161 }
 162 
 163 /*
 164  * kstat_network_port_state: returns kstat info of a network port
 165  * 1 = up, 0 = down, anything else = unknown
 166  */
 167 static int
 168 kstat_network_port_state(kstat_ctl_t *kc, char *ks_module, int ks_instance)
 169 {
 170         kstat_named_t   *port_datap = NULL;
 171 
 172         if ((port_datap = kstat_name_lookup(kc, ks_module, ks_instance,
 173             LINK_UP)) == NULL) {
 174                 return (-1);
 175         }
 176         if (port_datap == NULL) {
 177                 return (-1);
 178         }
 179         if (port_datap->data_type == KSTAT_DATA_UINT32) {
 180                 if (port_datap->value.ui32 == 1) {
 181                         return (1);
 182                 } else if (port_datap->value.ui32 == 0) {
 183                         return (0);
 184                 } else {
 185                         return (-1);
 186                 }
 187         } else {
 188                 if (port_datap->value.ui64 == 1) {
 189                         return (1);
 190                 } else if (port_datap->value.ui64 == 0) {
 191                         return (0);
 192                 } else {
 193                         return (-1);
 194                 }
 195         }
 196 }
 197 
 198 /*
 199  * kstat_network_port_cond: returns kstat info of a network port
 200  * 0 = OK, 1 = FAILING, 2 = FAILED, 3 = TESTING, -1 = unknown
 201  */
 202 static int
 203 kstat_network_port_cond(kstat_ctl_t *kc, char *ks_module, int ks_instance)
 204 {
 205         kstat_named_t   *port_datap = NULL;
 206         uint64_t        collisions, runt, link_up, link_duplex;
 207         uint64_t        ifspeed, ierrors, ipackets, oerrors, opackets;
 208 
 209         if ((port_datap = kstat_name_lookup(kc, ks_module, ks_instance,
 210             LINK_UP)) == NULL) {
 211                 return (-1);
 212         }
 213 
 214         if (port_datap->data_type == KSTAT_DATA_UINT32) {
 215                 link_up = port_datap->value.ui32;
 216         } else {
 217                 link_up = port_datap->value.ui64;
 218         }
 219         if (link_up == 0) {
 220                 return (2);
 221         }
 222 
 223         if ((port_datap = kstat_name_lookup(kc, ks_module, ks_instance,
 224             DUPLEX)) == NULL) {
 225                 return (-1);
 226         }
 227 
 228         if (port_datap->data_type == KSTAT_DATA_UINT32) {
 229                 link_duplex = port_datap->value.ui32;
 230         } else {
 231                 link_duplex = port_datap->value.ui64;
 232         }
 233         if (link_duplex == 0) {
 234                 return (2);
 235         }
 236 
 237         if ((port_datap = kstat_name_lookup(kc, ks_module, ks_instance,
 238             IF_SPEED)) == NULL) {
 239                 return (-1);
 240         }
 241         if (port_datap->data_type == KSTAT_DATA_UINT32) {
 242                 ifspeed = port_datap->value.ui32;
 243         } else {
 244                 ifspeed = port_datap->value.ui64;
 245         }
 246         if (ifspeed == 0) {
 247                 return (2);
 248         }
 249 
 250         /* check for FAILING conditions */
 251         if ((port_datap = kstat_name_lookup(kc, ks_module, ks_instance,
 252             IERRORS)) == NULL) {
 253                 return (-1);
 254         }
 255         if (port_datap->data_type == KSTAT_DATA_UINT32) {
 256                 ierrors = port_datap->value.ui32;
 257         } else {
 258                 ierrors = port_datap->value.ui64;
 259         }
 260 
 261         if ((port_datap = kstat_name_lookup(kc, ks_module, ks_instance,
 262             IPACKETS)) == NULL) {
 263                 return (-1);
 264         }
 265 
 266         if (port_datap->data_type == KSTAT_DATA_UINT32) {
 267                 ipackets = port_datap->value.ui32;
 268         } else {
 269                 ipackets = port_datap->value.ui64;
 270         }
 271         if (ierrors > ipackets/10) {
 272                 return (1);
 273         }
 274 
 275         if ((port_datap = kstat_name_lookup(kc, ks_module, ks_instance,
 276             OERRORS)) == NULL) {
 277                 return (-1);
 278         }
 279         if (port_datap->data_type == KSTAT_DATA_UINT32) {
 280                 oerrors = port_datap->value.ui32;
 281         } else {
 282                 oerrors = port_datap->value.ui64;
 283         }
 284 
 285         if ((port_datap = kstat_name_lookup(kc, ks_module, ks_instance,
 286             OPACKETS)) == NULL) {
 287                 return (-1);
 288         }
 289         if (port_datap->data_type == KSTAT_DATA_UINT32) {
 290                 opackets = port_datap->value.ui32;
 291         } else {
 292                 opackets = port_datap->value.ui64;
 293         }
 294         if (oerrors > opackets/10) {
 295                 return (1);
 296         }
 297 
 298         if ((port_datap = kstat_name_lookup(kc, ks_module, ks_instance,
 299             RUNT_ERRORS)) == NULL) {
 300                 return (-1);
 301         }
 302         if (port_datap->data_type == KSTAT_DATA_UINT32) {
 303                 runt = port_datap->value.ui32;
 304         } else {
 305                 runt = port_datap->value.ui64;
 306         }
 307         if (runt > ipackets/10) {
 308                 return (1);
 309         }
 310 
 311         if ((port_datap = kstat_name_lookup(kc, ks_module, ks_instance,
 312             COLLISIONS)) == NULL) {
 313                 return (-1);
 314         }
 315         if (port_datap->data_type == KSTAT_DATA_UINT32) {
 316                 collisions = port_datap->value.ui32;
 317         } else {
 318                 collisions = port_datap->value.ui64;
 319         }
 320         if (collisions > (opackets+ipackets)/30) {
 321                 return (1);
 322         }
 323         return (0);
 324 }
 325 
 326 /*
 327  * serial_port_state: returns status a serial port
 328  * 1 = up, 0 = down, anything else = unknown
 329  */
 330 
 331 /* ARGSUSED */
 332 static int
 333 serial_port_state(kstat_ctl_t *kc, char *driver, int instance)
 334 {
 335         int                     fd;
 336         char                    device[20];
 337         struct termios          flags;
 338         struct sigaction        old_sa, new_sa;
 339 
 340         (void) memset(&old_sa, 0, sizeof (old_sa));
 341         (void) memset(&new_sa, 0, sizeof (new_sa));
 342         new_sa.sa_handler = sig_alarm_handler;
 343         (void) sigaction(SIGALRM, &new_sa, &old_sa);
 344         (void) alarm(1);
 345 
 346         (void) snprintf(device, sizeof (device), "/dev/tty%c", instance+'a');
 347         fd = open(device, O_RDONLY|O_NDELAY|O_NONBLOCK|O_NOCTTY);
 348 
 349         /* Restore sig action flags */
 350         (void) sigaction(SIGALRM, &old_sa, (struct sigaction *)0);
 351         /* Disable alarm */
 352         (void) alarm(0);
 353 
 354         if (fd == -1) {
 355                 return (-1);
 356         }
 357 
 358         if (isatty(fd) == 0) {
 359                 (void) close(fd);
 360                 return (-1);
 361         }
 362         (void) memset(&flags, 0, sizeof (flags));
 363         if (ioctl(fd, TCGETS, &flags) != 0) {
 364                 (void) close(fd);
 365                 return (-1);
 366         }
 367         (void) close(fd);
 368         return ((flags.c_cflag & TIOCM_LE) ? 1 : 0);
 369 }
 370 
 371 /* ARGSUSED */
 372 static void
 373 sig_alarm_handler(int signo)
 374 {
 375 }
 376 
 377 /*
 378  * serial_port_cond: returns status of a serial port
 379  * 0 = OK, 1 = FAILING, 2 = FAILED, 3 = TESTING, anything else = UNKNOWN
 380  */
 381 static int
 382 serial_port_cond(kstat_ctl_t *kc, char *driver, int instance)
 383 {
 384         switch (serial_port_state(kc, driver, instance)) {
 385         case 1:
 386                 return (0);
 387         default:
 388                 return (-1);
 389         }
 390 }
 391 
 392 /*
 393  * parallel_port_state: returns kstat info of a serial port
 394  * 1 = up, 0 = down, anything else = unknown
 395  */
 396 static int
 397 parallel_port_state(kstat_ctl_t *kc, char *ks_module, int ks_instance)
 398 {
 399         kstat_t         *ksp = NULL;
 400         kstat_named_t   *port_datap = NULL;
 401         char            *data_lookup;
 402         char            ks_name[20];
 403 
 404         (void) snprintf(ks_name, sizeof (ks_name), "%s%d", ks_module,
 405             ks_instance);
 406         if ((ksp = kstat_lookup(kc, ks_module, ks_instance, ks_name)) == NULL) {
 407                 return (-1);
 408         }
 409         if (kstat_read(kc, ksp, NULL) == -1) {
 410                 return (-1);
 411         }
 412         data_lookup = "";
 413         port_datap = (kstat_named_t *)kstat_data_lookup(ksp, data_lookup);
 414         if (port_datap == NULL) {
 415                 return (-1);
 416         }
 417         return (-1);
 418 }
 419 
 420 /*
 421  * parallel_port_cond: returns kstat info of a serial port
 422  * 1 = up, 0 = down, anything else = unknown
 423  */
 424 static int
 425 parallel_port_cond(kstat_ctl_t *kc, char *ks_module, int ks_instance)
 426 {
 427         return (parallel_port_state(kc, ks_module, ks_instance));
 428 }