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 (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 27 /* All Rights Reserved */ 28 29 #include <stdio.h> 30 #include <stdlib.h> 31 #include <unistd.h> 32 #include <fcntl.h> 33 #include <errno.h> 34 #include <ctype.h> 35 #include <string.h> 36 #include <signal.h> 37 #include <utmpx.h> 38 #include <pwd.h> 39 #include <dirent.h> 40 #include <sys/param.h> 41 #include <sys/acl.h> 42 #include <sys/stat.h> 43 #include <sys/types.h> 44 #include <sys/mkdev.h> 45 #include <sys/console.h> 46 #include <libdevinfo.h> 47 #include "ttymon.h" 48 #include "tmextern.h" 49 #include "tmstruct.h" 50 51 static char devbuf[BUFSIZ]; 52 static char *devname; 53 54 static int parse_args(int, char **, struct pmtab *); 55 static void ttymon_options(int, char **, struct pmtab *); 56 static void getty_options(int, char **, struct pmtab *); 57 static void usage(void); 58 static char *find_ttyname(int); 59 60 /* 61 * ttymon_express - This is call when ttymon is invoked with args 62 * or invoked as getty 63 * - This special version of ttymon will monitor 64 * one port only 65 * - It is intended to be used when some process 66 * wants to have a login session on the fly 67 */ 68 void 69 ttymon_express(int argc, char **argv) 70 { 71 struct pmtab *pmtab; 72 struct sigaction sigact; 73 #ifdef DEBUG 74 #endif 75 76 #ifdef DEBUG 77 opendebug(TRUE); 78 #endif 79 80 sigact.sa_flags = 0; 81 sigact.sa_handler = SIG_IGN; 82 (void) sigemptyset(&sigact.sa_mask); 83 (void) sigaction(SIGINT, &sigact, NULL); 84 85 if ((pmtab = ALLOC_PMTAB) == NULL) { 86 log("ttymon_express: ALLOC_PMTAB failed"); 87 exit(1); 88 } 89 90 if (parse_args(argc, argv, pmtab) != 0) { 91 log("ttymon_express: parse_args failed"); 92 exit(1); 93 } 94 95 read_ttydefs(NULL, FALSE); 96 97 if ((pmtab->p_device != NULL) && (*(pmtab->p_device) != '\0')) 98 while (checkut_line(pmtab->p_device)) 99 (void) sleep(15); 100 101 if ((pmtab->p_device == NULL) || (*(pmtab->p_device) == '\0')) { 102 devname = find_ttyname(0); 103 if ((devname == NULL) || (*devname == '\0')) { 104 log("ttyname cannot find the device on fd 0"); 105 exit(1); 106 } 107 pmtab->p_device = devname; 108 #ifdef DEBUG 109 debug("ttymon_express: devname = %s", devname); 110 #endif 111 /* 112 * become session leader 113 * fd 0 is closed and reopened just to make sure 114 * controlling tty is set up right 115 */ 116 (void) setsid(); 117 (void) close(0); 118 revokedevaccess(pmtab->p_device, 0, 0, 0); 119 if (open(pmtab->p_device, O_RDWR) < 0) { 120 log("open %s failed: %s", pmtab->p_device, 121 strerror(errno)); 122 exit(1); 123 } 124 if ((pmtab->p_modules != NULL) && 125 (*(pmtab->p_modules) != '\0')) { 126 if (push_linedisc(0, pmtab->p_modules, 127 pmtab->p_device) == -1) 128 exit(1); 129 } 130 if (initial_termio(0, pmtab) == -1) 131 exit(1); 132 (void) di_devperm_logout((const char *)pmtab->p_device); 133 } else { 134 (void) setsid(); 135 (void) close(0); 136 Retry = FALSE; 137 open_device(pmtab); 138 if (Retry) /* open failed */ 139 exit(1); 140 } 141 tmchild(pmtab); 142 exit(1); /*NOTREACHED*/ 143 } 144 145 /* 146 * For serial device, return ttyX-mode property value. 147 */ 148 static char * 149 get_ttymode_prop(dev_t rconsdev) 150 { 151 char *rootpath = "/"; 152 char path[MAXPATHLEN]; 153 di_node_t root; 154 char *propname, *v; 155 struct stat st; 156 157 (void) snprintf(path, sizeof (path), "/dev/tty%c", 158 'a' + minor(rconsdev)); 159 if (stat(path, &st) < 0) 160 return (NULL); 161 162 if (st.st_rdev != rconsdev) 163 return (NULL); 164 165 if (asprintf(&propname, "%s-mode", path + 5) <= 0) 166 return (NULL); 167 168 root = di_init(rootpath, DINFOPROP); 169 if (root == DI_NODE_NIL) { 170 free(propname); 171 return (NULL); 172 } 173 174 v = NULL; 175 if (di_prop_lookup_strings(DDI_DEV_T_ANY, root, propname, &v) > 0) 176 v = strdup(v); 177 178 di_fini(root); 179 free(propname); 180 return (v); 181 } 182 183 /* 184 * parse_arg - parse cmd line arguments 185 */ 186 static int 187 parse_args(int argc, char **argv, struct pmtab *pmtab) 188 { 189 static char p_server[] = "/usr/bin/login"; 190 static char termbuf[MAX_TERM_TYPE_LEN]; 191 static struct cons_getterm cnterm = {sizeof (termbuf), termbuf}; 192 193 /* initialize fields to some default first */ 194 pmtab->p_tag = ""; 195 pmtab->p_flags = 0; 196 pmtab->p_identity = "root"; 197 pmtab->p_res1 = "reserved"; 198 pmtab->p_res2 = "reserved"; 199 pmtab->p_res3 = "reserved"; 200 pmtab->p_uid = 0; 201 pmtab->p_gid = 0; 202 pmtab->p_dir = "/"; 203 pmtab->p_ttyflags = 0; 204 pmtab->p_count = 0; 205 pmtab->p_server = p_server; 206 pmtab->p_timeout = 0; 207 pmtab->p_modules = ""; 208 pmtab->p_prompt = "login: "; 209 pmtab->p_dmsg = ""; 210 pmtab->p_termtype = ""; 211 pmtab->p_device = ""; 212 pmtab->p_status = GETTY; 213 pmtab->p_ttymode = NULL; 214 if (strcmp(lastname(argv[0]), "getty") == 0) { 215 pmtab->p_ttylabel = "300"; 216 getty_options(argc, argv, pmtab); 217 } else { 218 int cn_fd; 219 struct cons_getdev cnd; 220 221 pmtab->p_ttylabel = "9600"; 222 ttymon_options(argc, argv, pmtab); 223 224 /* 225 * The following code is only reached if -g was specified. 226 * It attempts to determine a suitable terminal type for 227 * the console login process, and in case we are using 228 * serial console, tty mode line. 229 * 230 * If -d /dev/console also specified, we send an ioctl 231 * to the console device to query the TERM type. 232 * 233 * If any of the tests, system calls, or ioctls fail 234 * then pmtab->p_termtype retains its default value 235 * of "". otherwise it is set to a term type value 236 * that was returned. 237 */ 238 if (strcmp(pmtab->p_device, "/dev/console") == 0 && 239 (cn_fd = open("/dev/console", O_RDONLY)) != -1) { 240 241 if (strlen(pmtab->p_termtype) == 0 && 242 ioctl(cn_fd, CONS_GETTERM, &cnterm) != -1) { 243 pmtab->p_termtype = cnterm.cn_term_type; 244 } 245 246 if (ioctl(cn_fd, CONS_GETDEV, &cnd) != -1) 247 pmtab->p_ttymode = 248 get_ttymode_prop(cnd.cnd_rconsdev); 249 (void) close(cn_fd); 250 } 251 } 252 253 if ((pmtab->p_device != NULL) && (*(pmtab->p_device) != '\0')) 254 getty_account(pmtab->p_device); /* utmp accounting */ 255 return (0); 256 } 257 258 259 /* 260 * ttymon_options - scan and check args for ttymon express 261 */ 262 263 static void 264 ttymon_options(int argc, char **argv, struct pmtab *pmtab) 265 { 266 int c; /* option letter */ 267 char *timeout; 268 int gflag = 0; /* -g seen */ 269 int size = 0; 270 char tbuf[BUFSIZ]; 271 272 while ((c = getopt(argc, argv, "T:gd:ht:p:m:l:")) != -1) { 273 switch (c) { 274 case 'g': 275 gflag = 1; 276 break; 277 case 'd': 278 pmtab->p_device = optarg; 279 break; 280 case 'h': 281 pmtab->p_ttyflags &= ~H_FLAG; 282 break; 283 284 case 'T': 285 pmtab->p_termtype = optarg; 286 break; 287 /* 288 * case 'b': 289 * pmtab->p_ttyflags |= B_FLAG; 290 * pmtab->p_ttyflags |= R_FLAG; 291 * break; 292 */ 293 case 't': 294 timeout = optarg; 295 while (*optarg) { 296 if (!isdigit(*optarg++)) { 297 log("Invalid argument for " 298 "\"-t\" -- number expected."); 299 usage(); 300 } 301 } 302 pmtab->p_timeout = atoi(timeout); 303 break; 304 case 'p': 305 copystr(tbuf, optarg); 306 pmtab->p_prompt = strsave(getword(tbuf, &size, TRUE)); 307 break; 308 case 'm': 309 pmtab->p_modules = optarg; 310 if (vml(pmtab->p_modules) != 0) 311 usage(); 312 break; 313 case 'l': 314 pmtab->p_ttylabel = optarg; 315 break; 316 case '?': 317 usage(); 318 break; /*NOTREACHED*/ 319 } 320 } 321 if (optind < argc) 322 usage(); 323 324 if (!gflag) 325 usage(); 326 } 327 328 /* 329 * usage - print out a usage message 330 */ 331 332 static void 333 usage(void) 334 { 335 char *umsg = "Usage: ttymon\n ttymon -g [-h] [-d device] " 336 "[-l ttylabel] [-t timeout] [-p prompt] [-m modules]\n"; 337 338 if (isatty(STDERR_FILENO)) 339 (void) fprintf(stderr, "%s", umsg); 340 else 341 cons_printf(umsg); 342 exit(1); 343 } 344 345 /* 346 * getty_options - this is cut from getty.c 347 * - it scan getty cmd args 348 * - modification is made to stuff args in pmtab 349 */ 350 static void 351 getty_options(int argc, char **argv, struct pmtab *pmtab) 352 { 353 char *ptr; 354 355 /* 356 * the pre-4.0 getty's hang_up_line() is a no-op. 357 * For compatibility, H_FLAG cannot be set for this "getty". 358 */ 359 pmtab->p_ttyflags &= ~(H_FLAG); 360 361 while (--argc && **++argv == '-') { 362 for (ptr = *argv + 1; *ptr; ptr++) { 363 switch (*ptr) { 364 case 'h': 365 break; 366 case 't': 367 if (isdigit(*++ptr)) { 368 (void) sscanf(ptr, "%d", 369 &(pmtab->p_timeout)); 370 while (isdigit(*++ptr)) 371 ; 372 ptr--; 373 } else if (--argc) { 374 if (isdigit(*(ptr = *++argv))) 375 (void) sscanf(ptr, "%d", 376 &(pmtab->p_timeout)); 377 else { 378 log("getty: timeout argument " 379 "<%s> invalid", *argv); 380 exit(1); 381 } 382 } 383 break; 384 385 case 'c': 386 log("Use \"sttydefs -l\" to check " 387 "/etc/ttydefs."); 388 exit(0); 389 default: 390 break; 391 } 392 } 393 } 394 395 if (argc < 1) { 396 log("getty: no terminal line specified."); 397 exit(1); 398 } else { 399 (void) strcat(devbuf, "/dev/"); 400 (void) strcat(devbuf, *argv); 401 pmtab->p_device = devbuf; 402 } 403 404 if (--argc > 0) { 405 pmtab->p_ttylabel = *++argv; 406 } 407 408 /* 409 * every thing after this will be ignored 410 * i.e. termtype and linedisc are ignored 411 */ 412 } 413 414 /* 415 * find_ttyname(fd) - find the name of device associated with fd. 416 * - it first tries utmpx to see if an entry exists 417 * - with my pid and ut_line is defined. If ut_line 418 * - is defined, it will see if the major and minor 419 * - number of fd and devname from utmpx match. 420 * - If utmpx search fails, ttyname(fd) will be called. 421 */ 422 static char * 423 find_ttyname(int fd) 424 { 425 pid_t ownpid; 426 struct utmpx *u; 427 static struct stat statf, statu; 428 static char buf[BUFSIZ]; 429 430 ownpid = getpid(); 431 setutxent(); 432 while ((u = getutxent()) != NULL) { 433 if (u->ut_pid == ownpid) { 434 if (strlen(u->ut_line) != 0) { 435 if (*(u->ut_line) != '/') { 436 (void) strcpy(buf, "/dev/"); 437 (void) strncat(buf, u->ut_line, 438 sizeof (u->ut_line)); 439 } else { 440 (void) strncat(buf, u->ut_line, 441 sizeof (u->ut_line)); 442 } 443 } 444 else 445 u = NULL; 446 break; 447 } 448 } 449 endutxent(); 450 if ((u != NULL) && 451 (fstat(fd, &statf) == 0) && 452 (stat(buf, &statu) == 0) && 453 (statf.st_dev == statu.st_dev) && 454 (statf.st_rdev == statu.st_rdev)) { 455 #ifdef DEBUG 456 debug("ttymon_express: find device name from utmpx."); 457 #endif 458 return (buf); 459 } else { 460 #ifdef DEBUG 461 debug("ttymon_express: calling ttyname to find device name."); 462 #endif 463 return (ttyname(fd)); 464 } 465 } 466 467 /* 468 * Revoke all access to a device node and make sure that there are 469 * no interposed streams devices attached. Must be called before a 470 * device is actually opened. 471 * When fdetach is called, the underlying device node is revealed; it 472 * will have the previous owner and that owner can re-attach; so we 473 * retry until we win. 474 * Ignore non-existent devices. 475 */ 476 void 477 revokedevaccess(char *dev, uid_t uid, gid_t gid, mode_t mode) 478 { 479 do { 480 if (chown(dev, uid, gid) == -1) 481 return; 482 } while (fdetach(dev) == 0); 483 484 /* Remove ACLs */ 485 486 (void) acl_strip(dev, uid, gid, mode); 487 } 488